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

markup_oops.pl (8110B)


      1#!/usr/bin/env perl
      2# SPDX-License-Identifier: GPL-2.0-only
      3
      4use File::Basename;
      5use Math::BigInt;
      6use Getopt::Long;
      7
      8# Copyright 2008, Intel Corporation
      9#
     10# This file is part of the Linux kernel
     11#
     12# Authors:
     13# 	Arjan van de Ven <arjan@linux.intel.com>
     14
     15
     16my $cross_compile = "";
     17my $vmlinux_name = "";
     18my $modulefile = "";
     19
     20# Get options
     21Getopt::Long::GetOptions(
     22	'cross-compile|c=s'	=> \$cross_compile,
     23	'module|m=s'		=> \$modulefile,
     24	'help|h'		=> \&usage,
     25) || usage ();
     26my $vmlinux_name = $ARGV[0];
     27if (!defined($vmlinux_name)) {
     28	my $kerver = `uname -r`;
     29	chomp($kerver);
     30	$vmlinux_name = "/lib/modules/$kerver/build/vmlinux";
     31	print "No vmlinux specified, assuming $vmlinux_name\n";
     32}
     33my $filename = $vmlinux_name;
     34
     35# Parse the oops to find the EIP value
     36
     37my $target = "0";
     38my $function;
     39my $module = "";
     40my $func_offset = 0;
     41my $vmaoffset = 0;
     42
     43my %regs;
     44
     45
     46sub parse_x86_regs
     47{
     48	my ($line) = @_;
     49	if ($line =~ /EAX: ([0-9a-f]+) EBX: ([0-9a-f]+) ECX: ([0-9a-f]+) EDX: ([0-9a-f]+)/) {
     50		$regs{"%eax"} = $1;
     51		$regs{"%ebx"} = $2;
     52		$regs{"%ecx"} = $3;
     53		$regs{"%edx"} = $4;
     54	}
     55	if ($line =~ /ESI: ([0-9a-f]+) EDI: ([0-9a-f]+) EBP: ([0-9a-f]+) ESP: ([0-9a-f]+)/) {
     56		$regs{"%esi"} = $1;
     57		$regs{"%edi"} = $2;
     58		$regs{"%esp"} = $4;
     59	}
     60	if ($line =~ /RAX: ([0-9a-f]+) RBX: ([0-9a-f]+) RCX: ([0-9a-f]+)/) {
     61		$regs{"%eax"} = $1;
     62		$regs{"%ebx"} = $2;
     63		$regs{"%ecx"} = $3;
     64	}
     65	if ($line =~ /RDX: ([0-9a-f]+) RSI: ([0-9a-f]+) RDI: ([0-9a-f]+)/) {
     66		$regs{"%edx"} = $1;
     67		$regs{"%esi"} = $2;
     68		$regs{"%edi"} = $3;
     69	}
     70	if ($line =~ /RBP: ([0-9a-f]+) R08: ([0-9a-f]+) R09: ([0-9a-f]+)/) {
     71		$regs{"%r08"} = $2;
     72		$regs{"%r09"} = $3;
     73	}
     74	if ($line =~ /R10: ([0-9a-f]+) R11: ([0-9a-f]+) R12: ([0-9a-f]+)/) {
     75		$regs{"%r10"} = $1;
     76		$regs{"%r11"} = $2;
     77		$regs{"%r12"} = $3;
     78	}
     79	if ($line =~ /R13: ([0-9a-f]+) R14: ([0-9a-f]+) R15: ([0-9a-f]+)/) {
     80		$regs{"%r13"} = $1;
     81		$regs{"%r14"} = $2;
     82		$regs{"%r15"} = $3;
     83	}
     84}
     85
     86sub reg_name
     87{
     88	my ($reg) = @_;
     89	$reg =~ s/r(.)x/e\1x/;
     90	$reg =~ s/r(.)i/e\1i/;
     91	$reg =~ s/r(.)p/e\1p/;
     92	return $reg;
     93}
     94
     95sub process_x86_regs
     96{
     97	my ($line, $cntr) = @_;
     98	my $str = "";
     99	if (length($line) < 40) {
    100		return ""; # not an asm istruction
    101	}
    102
    103	# find the arguments to the instruction
    104	if ($line =~ /([0-9a-zA-Z\,\%\(\)\-\+]+)$/) {
    105		$lastword = $1;
    106	} else {
    107		return "";
    108	}
    109
    110	# we need to find the registers that get clobbered,
    111	# since their value is no longer relevant for previous
    112	# instructions in the stream.
    113
    114	$clobber = $lastword;
    115	# first, remove all memory operands, they're read only
    116	$clobber =~ s/\([a-z0-9\%\,]+\)//g;
    117	# then, remove everything before the comma, thats the read part
    118	$clobber =~ s/.*\,//g;
    119
    120	# if this is the instruction that faulted, we haven't actually done
    121	# the write yet... nothing is clobbered.
    122	if ($cntr == 0) {
    123		$clobber = "";
    124	}
    125
    126	foreach $reg (keys(%regs)) {
    127		my $clobberprime = reg_name($clobber);
    128		my $lastwordprime = reg_name($lastword);
    129		my $val = $regs{$reg};
    130		if ($val =~ /^[0]+$/) {
    131			$val = "0";
    132		} else {
    133			$val =~ s/^0*//;
    134		}
    135
    136		# first check if we're clobbering this register; if we do
    137		# we print it with a =>, and then delete its value
    138		if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) {
    139			if (length($val) > 0) {
    140				$str = $str . " $reg => $val ";
    141			}
    142			$regs{$reg} = "";
    143			$val = "";
    144		}
    145		# now check if we're reading this register
    146		if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) {
    147			if (length($val) > 0) {
    148				$str = $str . " $reg = $val ";
    149			}
    150		}
    151	}
    152	return $str;
    153}
    154
    155# parse the oops
    156while (<STDIN>) {
    157	my $line = $_;
    158	if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
    159		$target = $1;
    160	}
    161	if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) {
    162		$target = $1;
    163	}
    164	if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) {
    165		$function = $1;
    166		$func_offset = $2;
    167	}
    168	if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\]  \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) {
    169		$function = $1;
    170		$func_offset = $2;
    171	}
    172
    173	# check if it's a module
    174	if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
    175		$module = $3;
    176	}
    177	if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\]  \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
    178		$module = $3;
    179	}
    180	parse_x86_regs($line);
    181}
    182
    183my $decodestart = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$func_offset");
    184my $decodestop = Math::BigInt->from_hex("0x$target") + 8192;
    185if ($target eq "0") {
    186	print "No oops found!\n";
    187	usage();
    188}
    189
    190# if it's a module, we need to find the .ko file and calculate a load offset
    191if ($module ne "") {
    192	if ($modulefile eq "") {
    193		$modulefile = `modinfo -F filename $module`;
    194		chomp($modulefile);
    195	}
    196	$filename = $modulefile;
    197	if ($filename eq "") {
    198		print "Module .ko file for $module not found. Aborting\n";
    199		exit;
    200	}
    201	# ok so we found the module, now we need to calculate the vma offset
    202	open(FILE, $cross_compile."objdump -dS $filename |") || die "Cannot start objdump";
    203	while (<FILE>) {
    204		if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) {
    205			my $fu = $1;
    206			$vmaoffset = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$fu") - Math::BigInt->from_hex("0x$func_offset");
    207		}
    208	}
    209	close(FILE);
    210}
    211
    212my $counter = 0;
    213my $state   = 0;
    214my $center  = -1;
    215my @lines;
    216my @reglines;
    217
    218sub InRange {
    219	my ($address, $target) = @_;
    220	my $ad = "0x".$address;
    221	my $ta = "0x".$target;
    222	my $delta = Math::BigInt->from_hex($ad) - Math::BigInt->from_hex($ta);
    223
    224	if (($delta > -4096) && ($delta < 4096)) {
    225		return 1;
    226	}
    227	return 0;
    228}
    229
    230
    231
    232# first, parse the input into the lines array, but to keep size down,
    233# we only do this for 4Kb around the sweet spot
    234
    235open(FILE, $cross_compile."objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump";
    236
    237while (<FILE>) {
    238	my $line = $_;
    239	chomp($line);
    240	if ($state == 0) {
    241		if ($line =~ /^([a-f0-9]+)\:/) {
    242			if (InRange($1, $target)) {
    243				$state = 1;
    244			}
    245		}
    246	}
    247	if ($state == 1) {
    248		if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) {
    249			my $val = $1;
    250			if (!InRange($val, $target)) {
    251				last;
    252			}
    253			if ($val eq $target) {
    254				$center = $counter;
    255			}
    256		}
    257		$lines[$counter] = $line;
    258
    259		$counter = $counter + 1;
    260	}
    261}
    262
    263close(FILE);
    264
    265if ($counter == 0) {
    266	print "No matching code found \n";
    267	exit;
    268}
    269
    270if ($center == -1) {
    271	print "No matching code found \n";
    272	exit;
    273}
    274
    275my $start;
    276my $finish;
    277my $codelines = 0;
    278my $binarylines = 0;
    279# now we go up and down in the array to find how much we want to print
    280
    281$start = $center;
    282
    283while ($start > 1) {
    284	$start = $start - 1;
    285	my $line = $lines[$start];
    286	if ($line =~ /^([a-f0-9]+)\:/) {
    287		$binarylines = $binarylines + 1;
    288	} else {
    289		$codelines = $codelines + 1;
    290	}
    291	if ($codelines > 10) {
    292		last;
    293	}
    294	if ($binarylines > 20) {
    295		last;
    296	}
    297}
    298
    299
    300$finish = $center;
    301$codelines = 0;
    302$binarylines = 0;
    303while ($finish < $counter) {
    304	$finish = $finish + 1;
    305	my $line = $lines[$finish];
    306	if ($line =~ /^([a-f0-9]+)\:/) {
    307		$binarylines = $binarylines + 1;
    308	} else {
    309		$codelines = $codelines + 1;
    310	}
    311	if ($codelines > 10) {
    312		last;
    313	}
    314	if ($binarylines > 20) {
    315		last;
    316	}
    317}
    318
    319
    320my $i;
    321
    322
    323# start annotating the registers in the asm.
    324# this goes from the oopsing point back, so that the annotator
    325# can track (opportunistically) which registers got written and
    326# whos value no longer is relevant.
    327
    328$i = $center;
    329while ($i >= $start) {
    330	$reglines[$i] = process_x86_regs($lines[$i], $center - $i);
    331	$i = $i - 1;
    332}
    333
    334$i = $start;
    335while ($i < $finish) {
    336	my $line;
    337	if ($i == $center) {
    338		$line =  "*$lines[$i] ";
    339	} else {
    340		$line =  " $lines[$i] ";
    341	}
    342	print $line;
    343	if (defined($reglines[$i]) && length($reglines[$i]) > 0) {
    344		my $c = 60 - length($line);
    345		while ($c > 0) { print " "; $c = $c - 1; };
    346		print "| $reglines[$i]";
    347	}
    348	if ($i == $center) {
    349		print "<--- faulting instruction";
    350	}
    351	print "\n";
    352	$i = $i +1;
    353}
    354
    355sub usage {
    356	print <<EOT;
    357Usage:
    358  dmesg | perl $0 [OPTION] [VMLINUX]
    359
    360OPTION:
    361  -c, --cross-compile CROSS_COMPILE	Specify the prefix used for toolchain.
    362  -m, --module MODULE_DIRNAME		Specify the module filename.
    363  -h, --help				Help.
    364EOT
    365	exit;
    366}