cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dt_to_config (41787B)


      1#!/usr/bin/env perl
      2# SPDX-License-Identifier: GPL-2.0-only
      3
      4# Copyright 2016 by Frank Rowand
      5# Copyright 2016 by Gaurav Minocha
      6#
      7
      8use strict 'refs';
      9use strict subs;
     10
     11use Getopt::Long;
     12
     13$VUFX = "160610a";
     14
     15$script_name = $0;
     16$script_name =~ s|^.*/||;
     17
     18
     19# ----- constants for print_flags()
     20
     21# Position in string $pr_flags.  Range of 0..($num_pr_flags - 1).
     22$pr_flag_pos_mcompatible       = 0;
     23$pr_flag_pos_driver            = 1;
     24$pr_flag_pos_mdriver           = 2;
     25$pr_flag_pos_config            = 3;
     26$pr_flag_pos_mconfig           = 4;
     27$pr_flag_pos_node_not_enabled  = 5;
     28$pr_flag_pos_white_list        = 6;
     29$pr_flag_pos_hard_coded        = 7;
     30$pr_flag_pos_config_hard_coded = 8;
     31$pr_flag_pos_config_none       = 9;
     32$pr_flag_pos_config_m          = 10;
     33$pr_flag_pos_config_y          = 11;
     34$pr_flag_pos_config_test_fail  = 12;
     35
     36$num_pr_flags = $pr_flag_pos_config_test_fail + 1;
     37
     38# flags in @pr_flag_value must be unique values to allow simple regular
     39# expessions to work for --include_flags and --exclude_flags.
     40# Convention: use upper case letters for potential issues or problems.
     41
     42@pr_flag_value = ('M', 'd', 'D', 'c', 'C', 'E', 'W', 'H', 'x', 'n', 'm', 'y', 'F');
     43
     44@pr_flag_help = (
     45    "multiple compatibles found for this node",
     46    "driver found for this compatible",
     47    "multiple drivers found for this compatible",
     48    "kernel config found for this driver",
     49    "multiple config options found for this driver",
     50    "node is not enabled",
     51    "compatible is white listed",
     52    "matching driver and/or kernel config is hard coded",
     53    "kernel config hard coded in Makefile",
     54    "one or more kernel config file options is not set",
     55    "one or more kernel config file options is set to 'm'",
     56    "one or more kernel config file options is set to 'y'",
     57    "one of more kernel config file options fails to have correct value"
     58);
     59
     60
     61# -----
     62
     63%driver_config = ();   # driver config array, indexed by driver source file
     64%driver_count = ();    # driver_cnt, indexed by compatible
     65%compat_driver = ();   # compatible driver array, indexed by compatible
     66%existing_config = (); # existing config symbols present in given config file
     67                       # expected values are: "y", "m", a decimal number, a
     68                       # hex number, or a string
     69
     70# ----- magic compatibles, do not have a driver
     71#
     72# Will not search for drivers for these compatibles.
     73
     74%compat_white_list = (
     75       'none'                  => '1',
     76       'pci'                   => '1',
     77       'simple-bus'            => '1',
     78);
     79
     80# Will not search for drivers for these compatibles.
     81#
     82# These compatibles have a very large number of false positives.
     83#
     84# 'hardcoded_no_driver' is a magic value.  Other code knows this
     85# magic value.  Do not use 'no_driver' here!
     86#
     87# Revisit each 'hardcoded_no_driver' to see how the compatible
     88# is used.  Are there drivers that can be provided?
     89
     90%driver_hard_code_list = (
     91       'cache'                 => ['hardcoded_no_driver'],
     92       'eeprom'                => ['hardcoded_no_driver'],
     93       'gpio'                  => ['hardcoded_no_driver'],
     94       'gpio-keys'             => ['drivers/input/keyboard/gpio_keys.c'],
     95       'i2c-gpio'              => ['drivers/i2c/busses/i2c-gpio.c'],
     96       'isa'                   => ['arch/mips/mti-malta/malta-dt.c',
     97                                    'arch/x86/kernel/devicetree.c'],
     98       'led'                   => ['hardcoded_no_driver'],
     99       'm25p32'                => ['hardcoded_no_driver'],
    100       'm25p64'                => ['hardcoded_no_driver'],
    101       'm25p80'                => ['hardcoded_no_driver'],
    102       'mtd-ram'               => ['drivers/mtd/maps/physmap_of.c'],
    103       'pwm-backlight'         => ['drivers/video/backlight/pwm_bl.c'],
    104       'spidev'                => ['hardcoded_no_driver'],
    105       'syscon'                => ['drivers/mfd/syscon.c'],
    106       'tlv320aic23'           => ['hardcoded_no_driver'],
    107       'wm8731'                => ['hardcoded_no_driver'],
    108);
    109
    110# Use these config options instead of searching makefiles
    111
    112%driver_config_hard_code_list = (
    113
    114       # this one needed even if %driver_hard_code_list is empty
    115       'no_driver'                             => ['no_config'],
    116       'hardcoded_no_driver'                   => ['no_config'],
    117
    118       # drivers/usb/host/ehci-ppc-of.c
    119       # drivers/usb/host/ehci-xilinx-of.c
    120       #  are included from:
    121       #    drivers/usb/host/ehci-hcd.c
    122       #  thus the search of Makefile for the included .c files is incorrect
    123       # ehci-hcd.c wraps the includes with ifdef CONFIG_USB_EHCI_HCD_..._OF
    124       #
    125       # similar model for ohci-hcd.c (but no ohci-xilinx-of.c)
    126       #
    127       # similarly, uhci-hcd.c includes uhci-platform.c
    128
    129       'drivers/usb/host/ehci-ppc-of.c'        => ['CONFIG_USB_EHCI_HCD',
    130                                                   'CONFIG_USB_EHCI_HCD_PPC_OF'],
    131       'drivers/usb/host/ohci-ppc-of.c'        => ['CONFIG_USB_OHCI_HCD',
    132                                                   'CONFIG_USB_OHCI_HCD_PPC_OF'],
    133
    134       'drivers/usb/host/ehci-xilinx-of.c'     => ['CONFIG_USB_EHCI_HCD',
    135                                                   'CONFIG_USB_EHCI_HCD_XILINX'],
    136
    137       'drivers/usb/host/uhci-platform.c'      => ['CONFIG_USB_UHCI_HCD',
    138                                                   'CONFIG_USB_UHCI_PLATFORM'],
    139
    140       # scan_makefile will find only one of these config options:
    141       #    ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
    142       'arch/arm/mach-imx/platsmp.c'           => ['CONFIG_SOC_IMX6 && CONFIG_SMP',
    143                                                   'CONFIG_SOC_LS1021A && CONFIG_SMP'],
    144);
    145
    146
    147# 'virt/kvm/arm/.*' are controlled by makefiles in other directories,
    148# using relative paths, such as 'KVM := ../../../virt/kvm'.  Do not
    149# add complexity to find_kconfig() to deal with this.  There is a long
    150# term intent to change the kvm related makefiles to the normal kernel
    151# style.  After that is done, this entry can be removed from the
    152# black_list_driver.
    153
    154@black_list_driver = (
    155       # kvm no longer a problem after commit 503a62862e8f in 4.7-rc1
    156       # 'virt/kvm/arm/.*',
    157);
    158
    159
    160sub usage()
    161{
    162       print
    163"
    164Usage: $script_name [options] device-tree...
    165
    166    device_tree is: dts_file | dtb_file | proc_device-tree
    167
    168
    169Valid options:
    170     -c FILE             Read kernel config options from FILE
    171    --config FILE        synonym for 'c'
    172    --config-format      config file friendly output format
    173    --exclude-flag FLAG  exclude entries with a matching flag
    174     -h                  Display this message and exit
    175    --help               synonym for 'h'
    176    --black-list-driver  use driver black list
    177    --white-list-config  use config white list
    178    --white-list-driver  use driver white list
    179    --include-flag FLAG  include only entries with a matching flag
    180    --include-suspect    include only entries with an uppercase flag
    181    --short-name         do not show the path portion of the node name
    182    --show-lists         report of white and black lists
    183    --version            Display program version and exit
    184
    185
    186  Report driver source files that match the compatibles in the device
    187  tree file and the kernel config options that enable the driver source
    188  files.
    189
    190  This program must be run in the root directory of a Linux kernel
    191  source tree.
    192
    193  The default format is a report that is intended to be easily human
    194  scannable.
    195
    196  An alternate format can be selected by --config-format.  This will
    197  create output that can easily be edited to create a fragment that can
    198  be appended to the existing kernel config file.  Each entry consists of
    199  multiple lines.  The first line reports flags, the node path, compatible
    200  value, driver file matching the compatible, configuration options, and
    201  current values of the configuration options.  For each configuration
    202  option, the following lines report the current value and the value that
    203  is required for the driver file to be included in the kernel.
    204
    205  If a large number of drivers or config options is listed for a node,
    206  and the '$pr_flag_value[$pr_flag_pos_hard_coded]' flag is set consider using --white-list-config and/or
    207  --white-list-driver.  If the white list option suppresses the correct
    208  entry please report that as a bug.
    209
    210  CAUTION:
    211     This program uses heuristics to guess which driver(s) support each
    212     compatible string and which config option(s) enables the driver(s).
    213     Do not believe that the reported information is fully correct.
    214     This program is intended to aid the process of determining the
    215     proper kernel configuration for a device tree, but this is not
    216     a fully automated process -- human involvement may still be
    217     required!
    218
    219     The driver match heuristic used is to search for source files
    220     containing the compatible string enclosed in quotes.
    221
    222     This program might not be able to find all drivers matching a
    223     compatible string.
    224
    225     Some makefiles are overly clever.  This program was not made
    226     complex enough to handle them.  If no config option is listed
    227     for a driver, look at the makefile for the driver source file.
    228     Even if a config option is listed for a driver, some other
    229     available config options may not be listed.
    230
    231  FLAG values:
    232";
    233
    234       for ($k = 0; $k < $num_pr_flags; $k++) {
    235               printf "     %s   %s\n", $pr_flag_value[$k], $pr_flag_help[$k];
    236       }
    237
    238       print
    239"
    240     Upper case letters indicate potential issues or problems.
    241
    242  The flag:
    243
    244";
    245
    246       $k = $pr_flag_pos_hard_coded;
    247       printf "     %s   %s\n", $pr_flag_value[$k], $pr_flag_help[$k];
    248
    249       print
    250"
    251  will be set if the config or driver is in the white lists, even if
    252  --white-list-config and --white-list-driver are not specified.
    253  This is a hint that 1) many of these reported lines are likely to
    254  be incorrect, and 2) using those options will reduce the number of
    255  drivers and/or config options reported.
    256
    257  --white-list-config and --white-list-driver may not be accurate if this
    258  program is not well maintained.  Use them with appropriate skepticism.
    259  Use the --show-lists option to report the values in the list.
    260
    261  Return value:
    262    0   if no error
    263    1   error processing command line
    264    2   unable to open or read kernel config file
    265    3   unable to open or process input device tree file(s)
    266
    267  EXAMPLES:
    268
    269     dt_to_config arch/arm/boot/dts/my_dts_file.dts
    270
    271       Basic report.
    272
    273     dt_to_config \\
    274        --config \${KBUILD_OUTPUT}/.config \\
    275        arch/\${ARCH}/boot/dts/my_dts_file.dts
    276
    277       Full report, with config file issues noted.
    278
    279     dt_to_config --include-suspect \\
    280        --config \${KBUILD_OUTPUT}/.config \\
    281        arch/\${ARCH}/boot/dts/my_dts_file.dts
    282
    283       Report of node / compatible string / driver tuples that should
    284       be further investigated.  A node may have multiple compatible
    285       strings.  A compatible string may be matched by multiple drivers.
    286       A driver may have config file issues noted.  The compatible string
    287       and/or driver may be in the white lists.
    288
    289     dt_to_config --include-suspect --config-format \\
    290        --config ${KBUILD_OUTPUT}/.config \\
    291        arch/\${ARCH}/boot/dts/my_dts_file.dts
    292
    293       Report of node / compatible string / driver tuples that should
    294       be further investigated.  The report can be edited to uncomment
    295       the config options to select the desired tuple for a given node.
    296       A node may have multiple compatible strings.  A compatible string
    297       may be matched by multiple drivers.  A driver may have config file
    298       issues noted.  The compatible string and/or driver may be in the
    299       white lists.
    300
    301";
    302}
    303
    304sub set_flag()
    305{
    306       # pr_flags_ref is a reference to $pr_flags
    307
    308       my $pr_flags_ref = shift;
    309       my $pos          = shift;
    310
    311       substr $$pr_flags_ref, $pos, 1, $pr_flag_value[$pos];
    312
    313       return $pr_flags;
    314}
    315
    316sub print_flags()
    317{
    318       # return 1 if anything printed, else 0
    319
    320       # some fields of pn_arg_ref might not be used in this function, but
    321       # extract all of them anyway.
    322       my $pn_arg_ref     = shift;
    323
    324       my $compat         = $pn_arg_ref->{compat};
    325       my $compatible_cnt = $pn_arg_ref->{compatible_cnt};
    326       my $config         = $pn_arg_ref->{config};
    327       my $config_cnt     = $pn_arg_ref->{config_cnt};
    328       my $driver         = $pn_arg_ref->{driver};
    329       my $driver_cnt     = $pn_arg_ref->{driver_cnt};
    330       my $full_node      = $pn_arg_ref->{full_node};
    331       my $node           = $pn_arg_ref->{node};
    332       my $node_enabled   = $pn_arg_ref->{node_enabled};
    333       my $white_list     = $pn_arg_ref->{white_list};
    334
    335       my $pr_flags       = '-' x $num_pr_flags;
    336
    337
    338       # ----- set flags in $pr_flags
    339
    340       if ($compatible_cnt > 1) {
    341               &set_flag(\$pr_flags, $pr_flag_pos_mcompatible);
    342       }
    343
    344       if ($config_cnt > 1) {
    345               &set_flag(\$pr_flags, $pr_flag_pos_mconfig);
    346       }
    347
    348       if ($driver_cnt >= 1) {
    349               &set_flag(\$pr_flags, $pr_flag_pos_driver);
    350       }
    351
    352       if ($driver_cnt > 1) {
    353               &set_flag(\$pr_flags, $pr_flag_pos_mdriver);
    354       }
    355
    356       # These strings are the same way the linux kernel tests.
    357       # The ePapr lists of values is slightly different.
    358       if (!(
    359             ($node_enabled eq "") ||
    360             ($node_enabled eq "ok") ||
    361             ($node_enabled eq "okay")
    362            )) {
    363               &set_flag(\$pr_flags, $pr_flag_pos_node_not_enabled);
    364       }
    365
    366       if ($white_list) {
    367               &set_flag(\$pr_flags, $pr_flag_pos_white_list);
    368       }
    369
    370       if (exists($driver_hard_code_list{$compat}) ||
    371           (exists($driver_config_hard_code_list{$driver}) &&
    372            ($driver ne "no_driver"))) {
    373               &set_flag(\$pr_flags, $pr_flag_pos_hard_coded);
    374       }
    375
    376       my @configs = split(' && ', $config);
    377       for $configs (@configs) {
    378               $not = $configs =~ /^!/;
    379               $configs =~ s/^!//;
    380
    381               if (($configs ne "no_config") && ($configs ne "no_makefile")) {
    382                       &set_flag(\$pr_flags, $pr_flag_pos_config);
    383               }
    384
    385               if (($config_cnt >= 1) &&
    386                   ($configs !~ /CONFIG_/) &&
    387                   (($configs ne "no_config") && ($configs ne "no_makefile"))) {
    388                       &set_flag(\$pr_flags, $pr_flag_pos_config_hard_coded);
    389               }
    390
    391               my $existing_config = $existing_config{$configs};
    392               if ($existing_config eq "m") {
    393                       &set_flag(\$pr_flags, $pr_flag_pos_config_m);
    394                       # Possible fail, depends on whether built in or
    395                       # module is desired.
    396                       &set_flag(\$pr_flags, $pr_flag_pos_config_test_fail);
    397               } elsif ($existing_config eq "y") {
    398                       &set_flag(\$pr_flags, $pr_flag_pos_config_y);
    399                       if ($not) {
    400                               &set_flag(\$pr_flags, $pr_flag_pos_config_test_fail);
    401                       }
    402               } elsif (($config_file) && ($configs =~ /CONFIG_/)) {
    403                       &set_flag(\$pr_flags, $pr_flag_pos_config_none);
    404                       if (!$not) {
    405                               &set_flag(\$pr_flags, $pr_flag_pos_config_test_fail);
    406                       }
    407               }
    408       }
    409
    410       # ----- include / exclude filters
    411
    412       if ($include_flag_pattern && ($pr_flags !~ m/$include_flag_pattern/)) {
    413               return 0;
    414       }
    415
    416       if ($exclude_flag_pattern && ($pr_flags =~ m/$exclude_flag_pattern/)) {
    417               return 0;
    418       }
    419
    420       if ($config_format) {
    421               print "# ";
    422       }
    423       print "$pr_flags : ";
    424
    425       return 1;
    426}
    427
    428
    429sub print_node()
    430{
    431       # return number of lines printed
    432
    433       # some fields of pn_arg_ref might not be used in this function, but
    434       # extract all of them anyway.
    435       my $pn_arg_ref     = shift;
    436
    437       my $compat         = $pn_arg_ref->{compat};
    438       my $compatible_cnt = $pn_arg_ref->{compatible_cnt};
    439       my $config         = $pn_arg_ref->{config};
    440       my $config_cnt     = $pn_arg_ref->{config_cnt};
    441       my $driver         = $pn_arg_ref->{driver};
    442       my $driver_cnt     = $pn_arg_ref->{driver_cnt};
    443       my $full_node      = $pn_arg_ref->{full_node};
    444       my $node           = $pn_arg_ref->{node};
    445       my $node_enabled   = $pn_arg_ref->{node_enabled};
    446       my $white_list     = $pn_arg_ref->{white_list};
    447
    448       my $separator;
    449
    450       if (! &print_flags($pn_arg_ref)) {
    451               return 0;
    452       }
    453
    454
    455       if ($short_name) {
    456               print "$node";
    457       } else {
    458               print "$full_node";
    459       }
    460       print " : $compat : $driver : $config : ";
    461
    462       my @configs = split(' && ', $config);
    463
    464       if ($config_file) {
    465               for $configs (@configs) {
    466                       $configs =~ s/^!//;
    467                       my $existing_config = $existing_config{$configs};
    468                       if (!$existing_config) {
    469                               # check for /-m/, /-y/, or /-objs/
    470                               if ($configs !~ /CONFIG_/) {
    471                                       $existing_config = "x";
    472                               };
    473                       };
    474                       if ($existing_config) {
    475                               print "$separator", "$existing_config";
    476                               $separator = ", ";
    477                       } else {
    478                               print "$separator", "n";
    479                               $separator = ", ";
    480                       }
    481               }
    482       } else {
    483               print "none";
    484       }
    485
    486       print "\n";
    487
    488       if ($config_format) {
    489               for $configs (@configs) {
    490                       $not = $configs =~ /^!/;
    491                       $configs =~ s/^!//;
    492                       my $existing_config = $existing_config{$configs};
    493
    494                       if ($not) {
    495                               if ($configs !~ /CONFIG_/) {
    496                                       print "# $configs\n";
    497                               } elsif ($existing_config eq "m") {
    498                                       print "# $configs is m\n";
    499                                       print "# $configs=n\n";
    500                               } elsif ($existing_config eq "y") {
    501                                       print "# $configs is set\n";
    502                                       print "# $configs=n\n";
    503                               } else {
    504                                       print "# $configs is not set\n";
    505                                       print "# $configs=n\n";
    506                               }
    507
    508                       } else {
    509                               if ($configs !~ /CONFIG_/) {
    510                                       print "# $configs\n";
    511                               } elsif ($existing_config eq "m") {
    512                                       print "# $configs is m\n";
    513                                       print "# $configs=y\n";
    514                               } elsif ($existing_config eq "y") {
    515                                       print "# $configs is set\n";
    516                                       print "# $configs=y\n";
    517                               } else {
    518                                       print "# $configs is not set\n";
    519                                       print "# $configs=y\n";
    520                               }
    521                       }
    522               }
    523       }
    524
    525       return 1;
    526}
    527
    528
    529sub scan_makefile
    530{
    531       my $pn_arg_ref    = shift;
    532       my $driver        = shift;
    533
    534       # ----- Find Kconfig symbols that enable driver
    535
    536       my ($dir, $base) = $driver =~ m{(.*)/(.*).c};
    537
    538       my $makefile = $dir . "/Makefile";
    539       if (! -r $makefile) {
    540               $makefile = $dir . "/Kbuild";
    541       }
    542       if (! -r $makefile) {
    543               my $config;
    544
    545               $config = 'no_makefile';
    546               push @{ $driver_config{$driver} }, $config;
    547               return;
    548       }
    549
    550       if (!open(MAKEFILE_FILE, "<", "$makefile")) {
    551               return;
    552       }
    553
    554       my $line;
    555       my @config;
    556       my @if_config;
    557       my @make_var;
    558
    559       NEXT_LINE:
    560       while ($next_line = <MAKEFILE_FILE>) {
    561               my $config;
    562               my $if_config;
    563               my $ifdef;
    564               my $ifeq;
    565               my $ifndef;
    566               my $ifneq;
    567               my $ifdef_config;
    568               my $ifeq_config;
    569               my $ifndef_config;
    570               my $ifneq_config;
    571
    572               chomp($next_line);
    573               $line = $line . $next_line;
    574               if ($next_line =~ /\\$/) {
    575                       $line =~ s/\\$/ /;
    576                       next NEXT_LINE;
    577               }
    578               if ($line =~ /^\s*#/) {
    579                       $line = "";
    580                       next NEXT_LINE;
    581               }
    582
    583               # -----  condition ... else ... endif
    584
    585               if ($line =~ /^([ ]\s*|)else\b/) {
    586                       $if_config = "!" . pop @if_config;
    587                       $if_config =~ s/^!!//;
    588                       push @if_config, $if_config;
    589                       $line =~ s/^([ ]\s*|)else\b//;
    590               }
    591
    592               ($null, $ifeq_config,  $ifeq_config_val )  = $line =~ /^([ ]\s*|)ifeq\b.*\b(CONFIG_[A-Za-z0-9_]*)(.*)/;
    593               ($null, $ifneq_config, $ifneq_config_val)  = $line =~ /^([ ]\s*|)ifneq\b.*\b(CONFIG_[A-Za-z0-9_]*)(.*)/;
    594               ($null, $ifdef_config)                     = $line =~ /^([ ]\s*|)ifdef\b.*\b(CONFIG_[A-Za-z0-9_]*)/;
    595               ($null, $ifndef_config)                    = $line =~ /^([ ]\s*|)ifndef\b.*\b(CONFIG_[A-Za-z0-9_]*)/;
    596
    597               ($null, $ifeq)   = $line =~ /^([ ]\s*|)ifeq\b\s*(.*)/;
    598               ($null, $ifneq)  = $line =~ /^([ ]\s*|)ifneq\b\s*(.*)/;
    599               ($null, $ifdef)  = $line =~ /^([ ]\s*|)ifdef\b\s*(.*)/;
    600               ($null, $ifndef) = $line =~ /^([ ]\s*|)ifndef\b\s*(.*)/;
    601
    602               # Order of tests is important.  Prefer "CONFIG_*" regex match over
    603               # less specific regex match.
    604               if ($ifdef_config) {
    605                       $if_config = $ifdef_config;
    606               } elsif ($ifeq_config) {
    607                       if ($ifeq_config_val =~ /y/) {
    608                               $if_config = $ifeq_config;
    609                       } else {
    610                               $if_config = "!" . $ifeq_config;
    611                       }
    612               } elsif ($ifndef_config) {
    613                       $if_config = "!" . $ifndef_config;
    614               } elsif ($ifneq_config) {
    615                       if ($ifneq_config_val =~ /y/) {
    616                               $if_config = "!" . $ifneq_config;
    617                       } else {
    618                               $if_config = $ifneq_config;
    619                       }
    620               } elsif ($ifdef) {
    621                       $if_config = $ifdef;
    622               } elsif ($ifeq) {
    623                       $if_config = $ifeq;
    624               } elsif ($ifndef) {
    625                       $if_config = "!" . $ifndef;
    626               } elsif ($ifneq) {
    627                       $if_config = "!" . $ifneq;
    628               } else {
    629                       $if_config = "";
    630               }
    631               $if_config =~ s/^!!//;
    632
    633               if ($if_config) {
    634                       push @if_config, $if_config;
    635                       $line = "";
    636                       next NEXT_LINE;
    637               }
    638
    639               if ($line =~ /^([ ]\s*|)endif\b/) {
    640                       pop @if_config;
    641                       $line = "";
    642                       next NEXT_LINE;
    643               }
    644
    645               # ----- simple CONFIG_* = *.[co]  or  xxx [+:?]*= *.[co]
    646               # Most makefiles select on *.o, but
    647               # arch/powerpc/boot/Makefile selects on *.c
    648
    649               ($config) = $line =~ /(CONFIG_[A-Za-z0-9_]+).*\b$base.[co]\b/;
    650
    651               # ----- match a make variable instead of *.[co]
    652               # Recursively expanded variables are not handled.
    653
    654               if (!$config) {
    655                       my $make_var;
    656                       ($make_var) = $line =~ /\s*(\S+?)\s*[+:\?]*=.*\b$base.[co]\b/;
    657                       if ($make_var) {
    658                               if ($make_var =~ /[a-zA-Z0-9]+-[ym]/) {
    659                                       $config = $make_var;
    660                               } elsif ($make_var =~ /[a-zA-Z0-9]+-objs/) {
    661                                       $config = $make_var;
    662                               } else {
    663                                       push @make_var, $make_var;
    664                               }
    665                       }
    666               }
    667
    668               if (!$config) {
    669                       for $make_var (@make_var) {
    670                               ($config) = $line =~ /(CONFIG_[A-Za-z0-9_]+).*\b$make_var\b/;
    671                               last if ($config);
    672                       }
    673               }
    674
    675               if (!$config) {
    676                       for $make_var (@make_var) {
    677                               ($config) = $line =~ /\s*(\S+?)\s*[+:\?]*=.*\b$make_var\b/;
    678                               last if ($config);
    679                       }
    680               }
    681
    682               # ----- next if no config found
    683
    684               if (!$config) {
    685                       $line = "";
    686                       next NEXT_LINE;
    687               }
    688
    689               for $if_config (@if_config) {
    690                       $config = $if_config . " && " . $config;
    691               }
    692
    693               push @{ $driver_config{$driver} }, $config;
    694
    695               $line = "";
    696       }
    697
    698       close(MAKEFILE_FILE);
    699
    700}
    701
    702
    703sub find_kconfig
    704{
    705       my $pn_arg_ref    = shift;
    706       my $driver        = shift;
    707
    708       my $lines_printed = 0;
    709       my @configs;
    710
    711       if (!@{ $driver_config{$driver} }) {
    712               &scan_makefile($pn_arg_ref, $driver);
    713               if (!@{ $driver_config{$driver} }) {
    714                       push @{ $driver_config{$driver} }, "no_config";
    715               }
    716       }
    717
    718       @configs = @{ $driver_config{$driver} };
    719
    720       $$pn_arg_ref{config_cnt} = $#configs + 1;
    721       for my $config (@configs) {
    722               $$pn_arg_ref{config} = $config;
    723               $lines_printed += &print_node($pn_arg_ref);
    724       }
    725
    726       return $lines_printed;
    727}
    728
    729
    730sub handle_compatible()
    731{
    732       my $full_node     = shift;
    733       my $node          = shift;
    734       my $compatible    = shift;
    735       my $node_enabled  = shift;
    736
    737       my $compat;
    738       my $lines_printed = 0;
    739       my %pn_arg        = ();
    740
    741       return if (!$node or !$compatible);
    742
    743       # Do not process compatible property of root node,
    744       # it is used to match board, not to bind a driver.
    745       return if ($node eq "/");
    746
    747       $pn_arg{full_node}    = $full_node;
    748       $pn_arg{node}         = $node;
    749       $pn_arg{node_enabled} = $node_enabled;
    750
    751       my @compatibles = split('", "', $compatible);
    752
    753       $compatibles[0] =~ s/^"//;
    754       $compatibles[$#compatibles] =~ s/"$//;
    755
    756       $pn_arg{compatible_cnt} = $#compatibles + 1;
    757
    758       COMPAT:
    759       for $compat (@compatibles) {
    760
    761               $pn_arg{compat}     = $compat;
    762               $pn_arg{driver_cnt} = 0;
    763               $pn_arg{white_list} = 0;
    764
    765               if (exists($compat_white_list{$compat})) {
    766                       $pn_arg{white_list} = 1;
    767                       $pn_arg{driver}     = "no_driver";
    768                       $pn_arg{config_cnt} = 1;
    769                       $pn_arg{config}     = "no_config";
    770                       $lines_printed += &print_node(\%pn_arg);
    771                       next COMPAT;
    772               }
    773
    774               # ----- if compat previously seen, use cached info
    775
    776               if (exists($compat_driver{$compat})) {
    777                       for my $driver (@{ $compat_driver{$compat} }) {
    778                               $pn_arg{driver}     = $driver;
    779                               $pn_arg{driver_cnt} = $driver_count{$compat};
    780                               $pn_arg{config_cnt} = $#{ $driver_config{$driver}} + 1;
    781
    782                               for my $config (@{ $driver_config{$driver} }) {
    783                                       $pn_arg{config} = $config;
    784                                       $lines_printed += &print_node(\%pn_arg);
    785                               }
    786
    787                               if (!@{ $driver_config{$driver} }) {
    788                                       # no config cached yet
    789                                       # $driver in %driver_hard_code_list
    790                                       # but not %driver_config_hard_code_list
    791                                       $lines_printed += &find_kconfig(\%pn_arg, $driver);
    792                               }
    793                       }
    794                       next COMPAT;
    795               }
    796
    797
    798               # ----- Find drivers (source files that contain compatible)
    799
    800               # this will miss arch/sparc/include/asm/parport.h
    801               # It is better to move the compatible out of the .h
    802               # than to add *.h. to the files list, because *.h generates
    803               # a lot of false negatives.
    804               my $files = '"*.c"';
    805               my $drivers = `git grep -l '"$compat"' -- $files`;
    806               chomp($drivers);
    807               if ($drivers eq "") {
    808                       $pn_arg{driver} = "no_driver";
    809                       $pn_arg{config_cnt} = 1;
    810                       $pn_arg{config} = "no_config";
    811                       push @{ $compat_driver{$compat} }, "no_driver";
    812                       $lines_printed += &print_node(\%pn_arg);
    813                       next COMPAT;
    814               }
    815
    816               my @drivers = split("\n", $drivers);
    817               $driver_count{$compat} = $#drivers + 1;
    818               $pn_arg{driver_cnt}    = $#drivers + 1;
    819
    820               DRIVER:
    821               for my $driver (@drivers) {
    822                       push @{ $compat_driver{$compat} }, $driver;
    823                       $pn_arg{driver} = $driver;
    824
    825                       # ----- if driver previously seen, use cached info
    826
    827                       $pn_arg{config_cnt} = $#{ $driver_config{$driver} } + 1;
    828                       for my $config (@{ $driver_config{$driver} }) {
    829                               $pn_arg{config} = $config;
    830                               $lines_printed += &print_node(\%pn_arg);
    831                       }
    832                       if (@{ $driver_config{$driver} }) {
    833                               next DRIVER;
    834                       }
    835
    836                       if ($black_list_driver) {
    837                               for $black (@black_list_driver) {
    838                                       next DRIVER if ($driver =~ /^$black$/);
    839                               }
    840                       }
    841
    842
    843                       # ----- Find Kconfig symbols that enable driver
    844
    845                       $lines_printed += &find_kconfig(\%pn_arg, $driver);
    846
    847               }
    848       }
    849
    850       # White space (line) between nodes for readability.
    851       # Each node may report several compatibles.
    852       # For each compatible, multiple drivers may be reported.
    853       # For each driver, multiple CONFIG_ options may be reported.
    854       if ($lines_printed) {
    855               print "\n";
    856       }
    857}
    858
    859sub read_dts()
    860{
    861       my $file         = shift;
    862
    863       my $compatible   = "";
    864       my $line;
    865       my $node         = "";
    866       my $node_enabled = "";
    867
    868       if (! -r $file) {
    869               print STDERR "file '$file' is not readable or does not exist\n";
    870               exit 3;
    871       }
    872
    873       if (!open(DT_FILE, "-|", "$dtx_diff $file")) {
    874               print STDERR "\n";
    875               print STDERR "shell command failed:\n";
    876               print STDERR "   $dtx_diff $file\n";
    877               print STDERR "\n";
    878               exit 3;
    879       }
    880
    881       FILE:
    882       while ($line = <DT_FILE>) {
    883               chomp($line);
    884
    885               if ($line =~ /{/) {
    886
    887                       &handle_compatible($full_node, $node, $compatible,
    888                                          $node_enabled);
    889
    890                       while ($end_node_count-- > 0) {
    891                               pop @full_node;
    892                       };
    893                       $end_node_count = 0;
    894                       $full_node = @full_node[-1];
    895
    896                       $node = $line;
    897                       $node =~ s/^\s*(.*)\s+\{.*/$1/;
    898                       $node =~ s/.*: //;
    899                       if ($node eq '/' ) {
    900                               $full_node = '/';
    901                       } elsif ($full_node ne '/') {
    902                               $full_node = $full_node . '/' . $node;
    903                       } else {
    904                               $full_node = '/' . $node;
    905                       }
    906                       push @full_node, $full_node;
    907
    908                       $compatible = "";
    909                       $node_enabled = "";
    910                       next FILE;
    911               }
    912
    913               if ($line =~ /}/) {
    914                       $end_node_count++;
    915               }
    916
    917               if ($line =~ /(\s+|^)status =/) {
    918                       $node_enabled = $line;
    919                       $node_enabled =~ s/^\t*//;
    920                       $node_enabled =~ s/^status = "//;
    921                       $node_enabled =~ s/";$//;
    922                       next FILE;
    923               }
    924
    925               if ($line =~ /(\s+|^)compatible =/) {
    926                       # Extract all compatible entries for this device
    927                       # White space matching here and in handle_compatible() is
    928                       # precise, because input format is the output of dtc,
    929                       # which is invoked by dtx_diff.
    930                       $compatible = $line;
    931                       $compatible =~ s/^\t*//;
    932                       $compatible =~ s/^compatible = //;
    933                       $compatible =~ s/;$//;
    934               }
    935       }
    936
    937       &handle_compatible($full_node, $node, $compatible, $node_enabled);
    938
    939       close(DT_FILE);
    940}
    941
    942
    943sub read_config_file()
    944{
    945       if (! -r $config_file) {
    946               print STDERR "file '$config_file' is not readable or does not exist\n";
    947               exit 2;
    948       }
    949
    950       if (!open(CONFIG_FILE, "<", "$config_file")) {
    951               print STDERR "open $config_file failed\n";
    952               exit 2;
    953       }
    954
    955       my @line;
    956
    957       LINE:
    958       while ($line = <CONFIG_FILE>) {
    959               chomp($line);
    960               next LINE if ($line =~ /^\s*#/);
    961               next LINE if ($line =~ /^\s*$/);
    962               @line = split /=/, $line;
    963               $existing_config{@line[0]} = @line[1];
    964       }
    965
    966       close(CONFIG_FILE);
    967}
    968
    969
    970sub cmd_line_err()
    971{
    972       my $msg = shift;
    973
    974       print STDERR "\n";
    975       print STDERR "   ERROR processing command line options\n";
    976       print STDERR "         $msg\n" if ($msg ne "");
    977       print STDERR "\n";
    978       print STDERR "   For help, type '$script_name --help'\n";
    979       print STDERR "\n";
    980}
    981
    982
    983# -----------------------------------------------------------------------------
    984# program entry point
    985
    986Getopt::Long::Configure("no_ignore_case", "bundling");
    987
    988if (!GetOptions(
    989       "c=s"               => \$config_file,
    990       "config=s"          => \$config_file,
    991       "config-format"     => \$config_format,
    992       "exclude-flag=s"    => \@exclude_flag,
    993       "h"                 => \$help,
    994       "help"              => \$help,
    995       "black-list-driver" => \$black_list_driver,
    996       "white-list-config" => \$white_list_config,
    997       "white-list-driver" => \$white_list_driver,
    998       "include-flag=s"    => \@include_flag,
    999       "include-suspect"   => \$include_suspect,
   1000       "short-name"        => \$short_name,
   1001       "show-lists"        => \$show_lists,
   1002       "version"           => \$version,
   1003       )) {
   1004
   1005       &cmd_line_err();
   1006
   1007       exit 1;
   1008}
   1009
   1010
   1011my $exit_after_messages = 0;
   1012
   1013if ($version) {
   1014       print STDERR "\n$script_name  $VUFX\n\n";
   1015       $exit_after_messages = 1;
   1016}
   1017
   1018
   1019if ($help) {
   1020       &usage;
   1021       $exit_after_messages = 1;
   1022}
   1023
   1024
   1025if ($show_lists) {
   1026
   1027       print "\n";
   1028       print "These compatibles are hard coded to have no driver.\n";
   1029       print "\n";
   1030       for my $compat (sort keys %compat_white_list) {
   1031               print "   $compat\n";
   1032       }
   1033
   1034
   1035       print "\n\n";
   1036       print "The driver for these compatibles is hard coded (white list).\n";
   1037       print "\n";
   1038       my $max_compat_len = 0;
   1039       for my $compat (sort keys %driver_hard_code_list) {
   1040               if (length $compat > $max_compat_len) {
   1041                       $max_compat_len = length $compat;
   1042               }
   1043       }
   1044       for my $compat (sort keys %driver_hard_code_list) {
   1045               if (($driver ne "hardcoded_no_driver") && ($driver ne "no_driver")) {
   1046                       my $first = 1;
   1047                       for my $driver (@{ $driver_hard_code_list{$compat} }) {
   1048                               if ($first) {
   1049                                       print "   $compat";
   1050                                       print " " x ($max_compat_len - length $compat);
   1051                                       $first = 0;
   1052                               } else {
   1053                                       print "   ", " " x $max_compat_len;
   1054                               }
   1055                               print "  $driver\n";
   1056                       }
   1057               }
   1058       }
   1059
   1060
   1061       print "\n\n";
   1062       print "The configuration option for these drivers is hard coded (white list).\n";
   1063       print "\n";
   1064       my $max_driver_len = 0;
   1065       for my $driver (sort keys %driver_config_hard_code_list) {
   1066               if (length $driver > $max_driver_len) {
   1067                       $max_driver_len = length $driver;
   1068               }
   1069       }
   1070       for my $driver (sort keys %driver_config_hard_code_list) {
   1071               if (($driver ne "hardcoded_no_driver") && ($driver ne "no_driver")) {
   1072                       my $first = 1;
   1073                       for my $config (@{ $driver_config_hard_code_list{$driver} }) {
   1074                               if ($first) {
   1075                                       print "   $driver";
   1076                                       print " " x ($max_driver_len - length $driver);
   1077                                       $first = 0;
   1078                               } else {
   1079                                       print "   ", " " x $max_driver_len;
   1080                               }
   1081                               print "  $config\n";
   1082                       }
   1083               }
   1084       }
   1085
   1086
   1087       print "\n\n";
   1088       print "These drivers are black listed.\n";
   1089       print "\n";
   1090       for my $driver (@black_list_driver) {
   1091               print "   $driver\n";
   1092       }
   1093
   1094       print "\n";
   1095
   1096       $exit_after_messages = 1;
   1097}
   1098
   1099
   1100if ($exit_after_messages) {
   1101       exit 0;
   1102}
   1103
   1104
   1105$exclude_flag_pattern = "[";
   1106for my $exclude_flag (@exclude_flag) {
   1107       $exclude_flag_pattern = $exclude_flag_pattern . $exclude_flag;
   1108}
   1109$exclude_flag_pattern = $exclude_flag_pattern . "]";
   1110# clean up if empty
   1111$exclude_flag_pattern =~ s/^\[\]$//;
   1112
   1113
   1114$include_flag_pattern = "[";
   1115for my $include_flag (@include_flag) {
   1116       $include_flag_pattern = $include_flag_pattern . $include_flag;
   1117}
   1118$include_flag_pattern = $include_flag_pattern . "]";
   1119# clean up if empty
   1120$include_flag_pattern =~ s/^\[\]$//;
   1121
   1122
   1123if ($exclude_flag_pattern) {
   1124       my $found = 0;
   1125       for $pr_flag_value (@pr_flag_value) {
   1126               if ($exclude_flag_pattern =~ m/$pr_flag_value/) {
   1127                       $found = 1;
   1128               }
   1129       }
   1130       if (!$found) {
   1131               &cmd_line_err("invalid value for FLAG in --exclude-flag\n");
   1132               exit 1
   1133       }
   1134}
   1135
   1136if ($include_flag_pattern) {
   1137       my $found = 0;
   1138       for $pr_flag_value (@pr_flag_value) {
   1139               if ($include_flag_pattern =~ m/$pr_flag_value/) {
   1140                       $found = 1;
   1141               }
   1142       }
   1143       if (!$found) {
   1144               &cmd_line_err("invalid value for FLAG in --include-flag\n");
   1145               exit 1
   1146       }
   1147}
   1148
   1149if ($include_suspect) {
   1150       $include_flag_pattern =~ s/\[//;
   1151       $include_flag_pattern =~ s/\]//;
   1152       $include_flag_pattern = "[" . $include_flag_pattern . "A-Z]";
   1153}
   1154
   1155if ($exclude_flag_pattern =~ m/$include_flag_pattern/) {
   1156       &cmd_line_err("the same flag appears in both --exclude-flag and --include-flag or --include-suspect\n");
   1157       exit 1
   1158}
   1159
   1160
   1161# ($#ARGV < 0) is valid for --help, --version
   1162if ($#ARGV < 0) {
   1163       &cmd_line_err("device-tree... is required");
   1164       exit 1
   1165}
   1166
   1167
   1168if ($config_file) {
   1169       &read_config_file();
   1170}
   1171
   1172
   1173# avoid pushing duplicates for this value
   1174$driver = "hardcoded_no_driver";
   1175for $config ( @{ $driver_config_hard_code_list{$driver} } ) {
   1176       push @{ $driver_config{$driver} }, $config;
   1177}
   1178
   1179if ($white_list_driver) {
   1180       for my $compat (keys %driver_hard_code_list) {
   1181               for my $driver (@{ $driver_hard_code_list{$compat} }) {
   1182                       push @{ $compat_driver{$compat} }, $driver;
   1183                       if ($driver ne "hardcoded_no_driver") {
   1184                               $driver_count{$compat} = scalar @{ $compat_driver{$compat} };
   1185                       }
   1186               }
   1187       }
   1188}
   1189
   1190if ($white_list_config) {
   1191       for my $driver (keys %driver_config_hard_code_list) {
   1192               if ($driver ne "hardcoded_no_driver") {
   1193                       for $config ( @{ $driver_config_hard_code_list{$driver} } ) {
   1194                               push @{ $driver_config{$driver} }, $config;
   1195                       }
   1196               }
   1197       }
   1198}
   1199
   1200if (-x "scripts/dtc/dtx_diff") {
   1201       $dtx_diff = "scripts/dtc/dtx_diff";
   1202} else {
   1203
   1204       print STDERR "\n";
   1205       print STDERR "$script_name must be run from the root directory of a Linux kernel tree\n";
   1206       print STDERR "\n";
   1207       exit 3;
   1208}
   1209
   1210for $file (@ARGV) {
   1211       &read_dts($file);
   1212}