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

get_feat.pl (14955B)


      1#!/usr/bin/perl
      2# SPDX-License-Identifier: GPL-2.0
      3
      4use strict;
      5use Pod::Usage;
      6use Getopt::Long;
      7use File::Find;
      8use Fcntl ':mode';
      9use Cwd 'abs_path';
     10
     11my $help;
     12my $man;
     13my $debug;
     14my $arch;
     15my $feat;
     16my $enable_fname;
     17
     18my $basename = abs_path($0);
     19$basename =~ s,/[^/]+$,/,;
     20
     21my $prefix=$basename . "../Documentation/features";
     22
     23# Used only at for full features output. The script will auto-adjust
     24# such values for the minimal possible values
     25my $status_size = 1;
     26my $description_size = 1;
     27
     28GetOptions(
     29	"debug|d+" => \$debug,
     30	"dir=s" => \$prefix,
     31	'help|?' => \$help,
     32	'arch=s' => \$arch,
     33	'feat=s' => \$feat,
     34	'feature=s' => \$feat,
     35	"enable-fname" => \$enable_fname,
     36	man => \$man
     37) or pod2usage(2);
     38
     39pod2usage(1) if $help;
     40pod2usage(-exitstatus => 0, -verbose => 2) if $man;
     41
     42pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
     43
     44my ($cmd, $arg) = @ARGV;
     45
     46pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"
     47		&& $cmd ne "ls" && $cmd ne "list");
     48
     49require Data::Dumper if ($debug);
     50
     51my %data;
     52my %archs;
     53
     54#
     55# Displays an error message, printing file name and line
     56#
     57sub parse_error($$$$) {
     58	my ($file, $ln, $msg, $data) = @_;
     59
     60	$data =~ s/\s+$/\n/;
     61
     62	print STDERR "Warning: file $file#$ln:\n\t$msg";
     63
     64	if ($data ne "") {
     65		print STDERR ". Line\n\t\t$data";
     66	} else {
     67	    print STDERR "\n";
     68	}
     69}
     70
     71#
     72# Parse a features file, storing its contents at %data
     73#
     74
     75my $h_name = "Feature";
     76my $h_kconfig = "Kconfig";
     77my $h_description = "Description";
     78my $h_subsys = "Subsystem";
     79my $h_status = "Status";
     80my $h_arch = "Architecture";
     81
     82my $max_size_name = length($h_name);
     83my $max_size_kconfig = length($h_kconfig);
     84my $max_size_description = length($h_description);
     85my $max_size_subsys = length($h_subsys);
     86my $max_size_status = length($h_status);
     87
     88my $max_size_arch = 0;
     89my $max_size_arch_with_header;
     90my $max_description_word = 0;
     91
     92sub parse_feat {
     93	my $file = $File::Find::name;
     94
     95	my $mode = (stat($file))[2];
     96	return if ($mode & S_IFDIR);
     97	return if ($file =~ m,($prefix)/arch-support.txt,);
     98	return if (!($file =~ m,arch-support.txt$,));
     99
    100	if ($enable_fname) {
    101		printf ".. FILE %s\n", abs_path($file);
    102	}
    103
    104	my $subsys = "";
    105	$subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
    106
    107	if (length($subsys) > $max_size_subsys) {
    108		$max_size_subsys = length($subsys);
    109	}
    110
    111	my $name;
    112	my $kconfig;
    113	my $description;
    114	my $comments = "";
    115	my $last_status;
    116	my $ln;
    117	my %arch_table;
    118
    119	print STDERR "Opening $file\n" if ($debug > 1);
    120	open IN, $file;
    121
    122	while(<IN>) {
    123		$ln++;
    124
    125		if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
    126			$name = $1;
    127			if (length($name) > $max_size_name) {
    128				$max_size_name = length($name);
    129			}
    130			next;
    131		}
    132		if (m/^\#\s+Kconfig:\s*(.*\S)/) {
    133			$kconfig = $1;
    134			if (length($kconfig) > $max_size_kconfig) {
    135				$max_size_kconfig = length($kconfig);
    136			}
    137			next;
    138		}
    139		if (m/^\#\s+description:\s*(.*\S)/) {
    140			$description = $1;
    141			if (length($description) > $max_size_description) {
    142				$max_size_description = length($description);
    143			}
    144
    145			foreach my $word (split /\s+/, $description) {
    146				if (length($word) > $max_description_word) {
    147					$max_description_word = length($word);
    148				}
    149			}
    150
    151			next;
    152		}
    153		next if (m/^\\s*$/);
    154		next if (m/^\s*\-+\s*$/);
    155		next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/);
    156
    157		if (m/^\#\s*(.*)/) {
    158			$comments .= "$1\n";
    159			next;
    160		}
    161		if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
    162			my $a = $1;
    163			my $status = $2;
    164
    165			if (length($status) > $max_size_status) {
    166				$max_size_status = length($status);
    167			}
    168			if (length($a) > $max_size_arch) {
    169				$max_size_arch = length($a);
    170			}
    171
    172			$status = "---" if ($status =~ m/^\.\.$/);
    173
    174			$archs{$a} = 1;
    175			$arch_table{$a} = $status;
    176			next;
    177		}
    178
    179		#Everything else is an error
    180		parse_error($file, $ln, "line is invalid", $_);
    181	}
    182	close IN;
    183
    184	if (!$name) {
    185		parse_error($file, $ln, "Feature name not found", "");
    186		return;
    187	}
    188
    189	parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
    190	parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
    191	parse_error($file, $ln, "Description not found", "") if (!$description);
    192
    193	if (!%arch_table) {
    194		parse_error($file, $ln, "Architecture table not found", "");
    195		return;
    196	}
    197
    198	$data{$name}->{where} = $file;
    199	$data{$name}->{subsys} = $subsys;
    200	$data{$name}->{kconfig} = $kconfig;
    201	$data{$name}->{description} = $description;
    202	$data{$name}->{comments} = $comments;
    203	$data{$name}->{table} = \%arch_table;
    204
    205	$max_size_arch_with_header = $max_size_arch + length($h_arch);
    206}
    207
    208#
    209# Output feature(s) for a given architecture
    210#
    211sub output_arch_table {
    212	my $title = "Feature status on $arch architecture";
    213
    214	print "=" x length($title) . "\n";
    215	print "$title\n";
    216	print "=" x length($title) . "\n\n";
    217
    218	print "=" x $max_size_subsys;
    219	print "  ";
    220	print "=" x $max_size_name;
    221	print "  ";
    222	print "=" x $max_size_kconfig;
    223	print "  ";
    224	print "=" x $max_size_status;
    225	print "  ";
    226	print "=" x $max_size_description;
    227	print "\n";
    228	printf "%-${max_size_subsys}s  ", $h_subsys;
    229	printf "%-${max_size_name}s  ", $h_name;
    230	printf "%-${max_size_kconfig}s  ", $h_kconfig;
    231	printf "%-${max_size_status}s  ", $h_status;
    232	printf "%-${max_size_description}s\n", $h_description;
    233	print "=" x $max_size_subsys;
    234	print "  ";
    235	print "=" x $max_size_name;
    236	print "  ";
    237	print "=" x $max_size_kconfig;
    238	print "  ";
    239	print "=" x $max_size_status;
    240	print "  ";
    241	print "=" x $max_size_description;
    242	print "\n";
    243
    244	foreach my $name (sort {
    245				($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
    246				("\L$a" cmp "\L$b")
    247			       } keys %data) {
    248		next if ($feat && $name ne $feat);
    249
    250		my %arch_table = %{$data{$name}->{table}};
    251		printf "%-${max_size_subsys}s  ", $data{$name}->{subsys};
    252		printf "%-${max_size_name}s  ", $name;
    253		printf "%-${max_size_kconfig}s  ", $data{$name}->{kconfig};
    254		printf "%-${max_size_status}s  ", $arch_table{$arch};
    255		printf "%-s\n", $data{$name}->{description};
    256	}
    257
    258	print "=" x $max_size_subsys;
    259	print "  ";
    260	print "=" x $max_size_name;
    261	print "  ";
    262	print "=" x $max_size_kconfig;
    263	print "  ";
    264	print "=" x $max_size_status;
    265	print "  ";
    266	print "=" x $max_size_description;
    267	print "\n";
    268}
    269
    270#
    271# list feature(s) for a given architecture
    272#
    273sub list_arch_features {
    274	print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#\n";
    275
    276	foreach my $name (sort {
    277				($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
    278				("\L$a" cmp "\L$b")
    279			       } keys %data) {
    280		next if ($feat && $name ne $feat);
    281
    282		my %arch_table = %{$data{$name}->{table}};
    283
    284		my $status = $arch_table{$arch};
    285		$status = " " x ((4 - length($status)) / 2) . $status;
    286
    287		printf " %${max_size_subsys}s/ ", $data{$name}->{subsys};
    288		printf "%-${max_size_name}s: ", $name;
    289		printf "%-5s|   ", $status;
    290		printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig};
    291		printf " %s\n", $data{$name}->{description};
    292	}
    293}
    294
    295#
    296# Output a feature on all architectures
    297#
    298sub output_feature {
    299	my $title = "Feature $feat";
    300
    301	print "=" x length($title) . "\n";
    302	print "$title\n";
    303	print "=" x length($title) . "\n\n";
    304
    305	print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
    306	print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
    307
    308	my $desc = $data{$feat}->{description};
    309	$desc =~ s/^([a-z])/\U$1/;
    310	$desc =~ s/\.?\s*//;
    311	print "\n$desc.\n\n";
    312
    313	my $com = $data{$feat}->{comments};
    314	$com =~ s/^\s+//;
    315	$com =~ s/\s+$//;
    316	if ($com) {
    317		print "Comments\n";
    318		print "--------\n\n";
    319		print "$com\n\n";
    320	}
    321
    322	print "=" x $max_size_arch_with_header;
    323	print "  ";
    324	print "=" x $max_size_status;
    325	print "\n";
    326
    327	printf "%-${max_size_arch}s  ", $h_arch;
    328	printf "%-${max_size_status}s", $h_status . "\n";
    329
    330	print "=" x $max_size_arch_with_header;
    331	print "  ";
    332	print "=" x $max_size_status;
    333	print "\n";
    334
    335	my %arch_table = %{$data{$feat}->{table}};
    336	foreach my $arch (sort keys %arch_table) {
    337		printf "%-${max_size_arch}s  ", $arch;
    338		printf "%-${max_size_status}s\n", $arch_table{$arch};
    339	}
    340
    341	print "=" x $max_size_arch_with_header;
    342	print "  ";
    343	print "=" x $max_size_status;
    344	print "\n";
    345}
    346
    347#
    348# Output all features for all architectures
    349#
    350
    351sub matrix_lines($$$) {
    352	my $desc_size = shift;
    353	my $status_size = shift;
    354	my $header = shift;
    355	my $fill;
    356	my $ln_marker;
    357
    358	if ($header) {
    359		$ln_marker = "=";
    360	} else {
    361		$ln_marker = "-";
    362	}
    363
    364	$fill = $ln_marker;
    365
    366	print "+";
    367	print $fill x $max_size_name;
    368	print "+";
    369	print $fill x $desc_size;
    370	print "+";
    371	print $ln_marker x $status_size;
    372	print "+\n";
    373}
    374
    375sub output_matrix {
    376	my $title = "Feature status on all architectures";
    377	my $notcompat = "Not compatible";
    378
    379	print "=" x length($title) . "\n";
    380	print "$title\n";
    381	print "=" x length($title) . "\n\n";
    382
    383	my $desc_title = "$h_kconfig / $h_description";
    384
    385	my $desc_size = $max_size_kconfig + 4;
    386	if (!$description_size) {
    387		$desc_size = $max_size_description if ($max_size_description > $desc_size);
    388	} else {
    389		$desc_size = $description_size if ($description_size > $desc_size);
    390	}
    391	$desc_size = $max_description_word if ($max_description_word > $desc_size);
    392
    393	$desc_size = length($desc_title) if (length($desc_title) > $desc_size);
    394
    395	$max_size_status = length($notcompat) if (length($notcompat) > $max_size_status);
    396
    397	# Ensure that the status will fit
    398	my $min_status_size = $max_size_status + $max_size_arch + 6;
    399	$status_size = $min_status_size if ($status_size < $min_status_size);
    400
    401
    402	my $cur_subsys = "";
    403	foreach my $name (sort {
    404				($data{$a}->{subsys} cmp $data{$b}->{subsys}) or
    405				("\L$a" cmp "\L$b")
    406			       } keys %data) {
    407
    408		if ($cur_subsys ne $data{$name}->{subsys}) {
    409			if ($cur_subsys ne "") {
    410				printf "\n";
    411			}
    412
    413			$cur_subsys = $data{$name}->{subsys};
    414
    415			my $title = "Subsystem: $cur_subsys";
    416			print "$title\n";
    417			print "=" x length($title) . "\n\n";
    418
    419
    420			matrix_lines($desc_size, $status_size, 0);
    421
    422			printf "|%-${max_size_name}s", $h_name;
    423			printf "|%-${desc_size}s", $desc_title;
    424
    425			printf "|%-${status_size}s|\n", "Status per architecture";
    426			matrix_lines($desc_size, $status_size, 1);
    427		}
    428
    429		my %arch_table = %{$data{$name}->{table}};
    430		my $cur_status = "";
    431
    432		my (@lines, @descs);
    433		my $line = "";
    434		foreach my $arch (sort {
    435					($arch_table{$b} cmp $arch_table{$a}) or
    436					("\L$a" cmp "\L$b")
    437				       } keys %arch_table) {
    438
    439			my $status = $arch_table{$arch};
    440
    441			if ($status eq "---") {
    442				$status = $notcompat;
    443			}
    444
    445			if ($status ne $cur_status) {
    446				if ($line ne "") {
    447					push @lines, $line;
    448					$line = "";
    449				}
    450				$line = "- **" . $status . "**: " . $arch;
    451			} elsif (length($line) + length ($arch) + 2 < $status_size) {
    452				$line .= ", " . $arch;
    453			} else {
    454				push @lines, $line;
    455				$line = "  " . $arch;
    456			}
    457			$cur_status = $status;
    458		}
    459		push @lines, $line if ($line ne "");
    460
    461		my $description = $data{$name}->{description};
    462		while (length($description) > $desc_size) {
    463			my $d = substr $description, 0, $desc_size;
    464
    465			# Ensure that it will end on a space
    466			# if it can't, it means that the size is too small
    467			# Instead of aborting it, let's print what we have
    468			if (!($d =~ s/^(.*)\s+.*/$1/)) {
    469				$d = substr $d, 0, -1;
    470				push @descs, "$d\\";
    471				$description =~ s/^\Q$d\E//;
    472			} else {
    473				push @descs, $d;
    474				$description =~ s/^\Q$d\E\s+//;
    475			}
    476		}
    477		push @descs, $description;
    478
    479		# Ensure that the full description will be printed
    480		push @lines, "" while (scalar(@lines) < 2 + scalar(@descs));
    481
    482		my $ln = 0;
    483		for my $line(@lines) {
    484			if (!$ln) {
    485				printf "|%-${max_size_name}s", $name;
    486				printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
    487			} elsif ($ln >= 2 && scalar(@descs)) {
    488				printf "|%-${max_size_name}s", "";
    489				printf "|%-${desc_size}s", shift @descs;
    490			} else {
    491				printf "|%-${max_size_name}s", "";
    492				printf "|%-${desc_size}s", "";
    493			}
    494
    495			printf "|%-${status_size}s|\n", $line;
    496
    497			$ln++;
    498		}
    499		matrix_lines($desc_size, $status_size, 0);
    500	}
    501}
    502
    503
    504#
    505# Parses all feature files located at $prefix dir
    506#
    507find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
    508
    509print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
    510
    511#
    512# Handles the command
    513#
    514if ($cmd eq "current") {
    515	$arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
    516	$arch =~s/\s+$//;
    517}
    518
    519if ($cmd eq "ls" or $cmd eq "list") {
    520	if (!$arch) {
    521		$arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
    522		$arch =~s/\s+$//;
    523	}
    524
    525	list_arch_features;
    526
    527	exit;
    528}
    529
    530if ($cmd ne "validate") {
    531	if ($arch) {
    532		output_arch_table;
    533	} elsif ($feat) {
    534		output_feature;
    535	} else {
    536		output_matrix;
    537	}
    538}
    539
    540__END__
    541
    542=head1 NAME
    543
    544get_feat.pl - parse the Linux Feature files and produce a ReST book.
    545
    546=head1 SYNOPSIS
    547
    548B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] [--arch=<arch>]
    549	       [--feature=<feature>|--feat=<feature>] <COMAND> [<ARGUMENT>]
    550
    551Where <COMMAND> can be:
    552
    553=over 8
    554
    555B<current>               - output table in ReST compatible ASCII format
    556			   with features for this machine's architecture
    557
    558B<rest>                  - output table(s)  in ReST compatible ASCII format
    559			   with features in ReST markup language. The output
    560			   is affected by --arch or --feat/--feature flags.
    561
    562B<validate>              - validate the contents of the files under
    563			   Documentation/features.
    564
    565B<ls> or B<list>         - list features for this machine's architecture,
    566			   using an easier to parse format.
    567			   The output is affected by --arch flag.
    568
    569=back
    570
    571=head1 OPTIONS
    572
    573=over 8
    574
    575=item B<--arch>
    576
    577Output features for an specific architecture, optionally filtering for
    578a single specific feature.
    579
    580=item B<--feat> or B<--feature>
    581
    582Output features for a single specific feature.
    583
    584=item B<--dir>
    585
    586Changes the location of the Feature files. By default, it uses
    587the Documentation/features directory.
    588
    589=item B<--enable-fname>
    590
    591Prints the file name of the feature files. This can be used in order to
    592track dependencies during documentation build.
    593
    594=item B<--debug>
    595
    596Put the script in verbose mode, useful for debugging. Can be called multiple
    597times, to increase verbosity.
    598
    599=item B<--help>
    600
    601Prints a brief help message and exits.
    602
    603=item B<--man>
    604
    605Prints the manual page and exits.
    606
    607=back
    608
    609=head1 DESCRIPTION
    610
    611Parse the Linux feature files from Documentation/features (by default),
    612optionally producing results at ReST format.
    613
    614It supports output data per architecture, per feature or a
    615feature x arch matrix.
    616
    617When used with B<rest> command, it will use either one of the tree formats:
    618
    619If neither B<--arch> or B<--feature> arguments are used, it will output a
    620matrix with features per architecture.
    621
    622If B<--arch> argument is used, it will output the features availability for
    623a given architecture.
    624
    625If B<--feat> argument is used, it will output the content of the feature
    626file using ReStructured Text markup.
    627
    628=head1 BUGS
    629
    630Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
    631
    632=head1 COPYRIGHT
    633
    634Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
    635
    636License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
    637
    638This is free software: you are free to change and redistribute it.
    639There is NO WARRANTY, to the extent permitted by law.
    640
    641=cut