| #!/usr/bin/env perl |
| # ---------------------------------------------------------------------- |
| # knlinfo by Phil Elwell for Raspberry Pi |
| # |
| # (c) 2014,2015 Raspberry Pi (Trading) Limited <info@raspberrypi.org> |
| # |
| # Licensed under the terms of the GNU General Public License. |
| # ---------------------------------------------------------------------- |
| |
| use strict; |
| use integer; |
| |
| use Fcntl ":seek"; |
| |
| my $trailer_magic = 'RPTL'; |
| |
| my %atom_formats = |
| ( |
| 'DDTK' => \&format_bool, |
| 'DTOK' => \&format_bool, |
| 'KVer' => \&format_string, |
| '270X' => \&format_bool, |
| '283X' => \&format_bool, |
| '283x' => \&format_bool, |
| ); |
| |
| if (@ARGV != 1) |
| { |
| print ("Usage: knlinfo <kernel image>\n"); |
| exit(1); |
| } |
| |
| my $kernel_file = $ARGV[0]; |
| |
| |
| my ($atoms, $pos) = read_trailer($kernel_file); |
| |
| exit(1) if (!$atoms); |
| |
| printf("Kernel trailer found at %d/0x%x:\n", $pos, $pos); |
| |
| foreach my $atom (@$atoms) |
| { |
| printf(" %s: %s\n", $atom->[0], format_atom($atom)); |
| } |
| |
| exit(0); |
| |
| sub read_trailer |
| { |
| my ($kernel_file) = @_; |
| my $fh; |
| |
| if (!open($fh, '<', $kernel_file)) |
| { |
| print ("* Failed to open '$kernel_file'\n"); |
| return undef; |
| } |
| |
| if (!seek($fh, -12, SEEK_END)) |
| { |
| print ("* seek error in '$kernel_file'\n"); |
| return undef; |
| } |
| |
| my $last_bytes; |
| sysread($fh, $last_bytes, 12); |
| |
| my ($trailer_len, $data_len, $magic) = unpack('VVa4', $last_bytes); |
| |
| if (($magic ne $trailer_magic) || ($data_len != 4)) |
| { |
| print ("* no trailer\n"); |
| return undef; |
| } |
| if (!seek($fh, -12, SEEK_END)) |
| { |
| print ("* seek error in '$kernel_file'\n"); |
| return undef; |
| } |
| |
| $trailer_len -= 12; |
| |
| while ($trailer_len > 0) |
| { |
| if ($trailer_len < 8) |
| { |
| print ("* truncated atom header in trailer\n"); |
| return undef; |
| } |
| if (!seek($fh, -8, SEEK_CUR)) |
| { |
| print ("* seek error in '$kernel_file'\n"); |
| return undef; |
| } |
| $trailer_len -= 8; |
| |
| my $atom_hdr; |
| sysread($fh, $atom_hdr, 8); |
| my ($atom_len, $atom_type) = unpack('Va4', $atom_hdr); |
| |
| if ($trailer_len < $atom_len) |
| { |
| print ("* truncated atom data in trailer\n"); |
| return undef; |
| } |
| |
| my $rounded_len = (($atom_len + 3) & ~3); |
| if (!seek($fh, -(8 + $rounded_len), SEEK_CUR)) |
| { |
| print ("* seek error in '$kernel_file'\n"); |
| return undef; |
| } |
| $trailer_len -= $rounded_len; |
| |
| my $atom_data; |
| sysread($fh, $atom_data, $atom_len); |
| |
| if (!seek($fh, -$atom_len, SEEK_CUR)) |
| { |
| print ("* seek error in '$kernel_file'\n"); |
| return undef; |
| } |
| |
| push @$atoms, [ $atom_type, $atom_data ]; |
| } |
| |
| if (($$atoms[-1][0] eq "\x00\x00\x00\x00") && |
| ($$atoms[-1][1] eq "")) |
| { |
| pop @$atoms; |
| } |
| else |
| { |
| print ("* end marker missing from trailer\n"); |
| } |
| |
| return ($atoms, tell($fh)); |
| } |
| |
| sub format_atom |
| { |
| my ($atom) = @_; |
| |
| my $format_func = $atom_formats{$atom->[0]} || \&format_hex; |
| return $format_func->($atom->[1]); |
| } |
| |
| sub format_bool |
| { |
| my ($data) = @_; |
| return unpack('V', $data) ? 'y' : 'n'; |
| } |
| |
| sub format_int |
| { |
| my ($data) = @_; |
| return unpack('V', $data); |
| } |
| |
| sub format_string |
| { |
| my ($data) = @_; |
| return '"'.$data.'"'; |
| } |
| |
| sub format_hex |
| { |
| my ($data) = @_; |
| return unpack('H*', $data); |
| } |