Skip to content

Instantly share code, notes, and snippets.

@liske
Last active July 14, 2022 16:59
Show Gist options
  • Save liske/6743d6dc692dead93228848dc480281c to your computer and use it in GitHub Desktop.
Save liske/6743d6dc692dead93228848dc480281c to your computer and use it in GitHub Desktop.
PoC - Check for matching AMD microcode updates.
#!/usr/bin/env perl
# Copyright Holder:
# 2019 (C) Thomas Liske [http://fiasko-nw.net/~thomas/]
#
# License:
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# This script is based on the result of the following paper:
#
# Security Analysis of x86 Processor Microcode
# Daming D. Chen <ddchen@asu.edu>
# Gail-Joon Ahn <gahn@asu.edu>
#
# https://www.dcddcc.com/docs/2014_paper_microcode.pdf
#
#
# The CPUID building from /proc/cpuinfo is based on the script `cpuinfo2cpuid`
# from the cpuid package written by
#
# Todd Allen <todd.allen@etallen.com>
# http://www.etallen.com/cpuid.html
#
use File::Basename;
use warnings;
use strict;
print("-=======[ SYSTEM ]=======-\n");
# get CPUID of cpu0 using kernel module
my $cpuid;
if ( open( my $fh, '<:raw', '/dev/cpu/0/cpuid' ) ) {
seek( $fh, 1, 0 );
read( $fh, my $eax, 16 );
close($fh);
$cpuid = unpack( 'V', $eax );
printf( "CPUID=%08x (/dev/cpu/0/cpuid)\n", $cpuid );
}
else {
warn "Failed to read CPUID (Missed \`modprobe cpuid\`?): $!\n";
}
# get CPUID from /proc/cpuinfo
{
open( my $fh, '<', '/proc/cpuinfo' )
|| die "Failed to read /proc/cpuinfo: $!\n";
my $vendor = '';
my $family = '';
my $model = '';
my $stepping = '';
# Read cpuinfo from STDIN
while (<$fh>) {
if (/^vendor_id\s*: (.*)$/) {
$vendor = $1;
}
if (/^cpu family\s*: (.*)$/) {
$family = $1;
}
if (/^model\s*: (.*)$/) {
$model = $1;
}
if (/^stepping\s*: (.*)$/) {
$stepping = $1;
}
# stop after cpu0
last if(/^$/);
}
my $xfamily = 0;
if($family > 0xf) {
$xfamily = $family - 0xf;
$family = 0xf;
}
my $xmodel = $model >> 4;
$model = $model & 0xf;
my $eax =
( ( ( $xfamily & 0xff ) << 20 ) +
( ( $xmodel & 0xf ) << 16 ) +
( ( $family & 0xf ) << 8 ) +
( ( $model & 0xf ) << 4 ) +
( ( $stepping & 0xf ) << 0 ) );
printf( "CPUID=%08x (/proc/cpuinfo)\n", $eax);
if($cpuid) {
if ($cpuid != $eax) {
warn "CPUID mismatch!\n";
}
}
else {
$cpuid = $eax;
}
}
# get microcode version of cpu0
open( my $fh, '<', '/proc/cpuinfo' );
my ($ucode) = grep { s/^microcode\s*:\s+(\S+)\s*/$1/s; } <$fh>;
close($fh);
$ucode = hex($ucode);
printf( "UCODE=%08x\n\n", $ucode );
# scan AMD ucode files
foreach my $fn (</lib/firmware/amd-ucode/microcode_*.bin>) {
my $bn = basename( $fn, '.bin' );
print("\n=======[ $bn ]=======-\n");
open( $fh, '<:raw', $fn )
|| die "Failed to open ucode source file '$fn': $!\n";
my @stat = stat($fh);
my $fpos = read( $fh, my $buf, 12 );
my ( $hdr_magic, $hdr_type, $hdr_size ) = unpack( 'a4VV', $buf );
die "Invalid magic header ($hdr_magic)!\n" if ( $hdr_magic ne "DMA\0" );
die "Unsupported table type $hdr_type!\n" if ( $hdr_type != 0 );
print("File size = $stat[7]\n");
print("Table size = $hdr_size\n\n");
my $prid;
for ( ; $fpos < $hdr_size ; ) {
$fpos += read( $fh, $buf, 16 );
my ( $pkg_cpuid, $pkg_errmask, $pkg_errcomp, $pkg_prid, $pkg_unk ) =
unpack( 'VVVvv', $buf );
if ( $pkg_cpuid > 0 ) {
my $mark = '';
# check ucode version if CPUID does match
if ( $cpuid == $pkg_cpuid ) {
$prid = $pkg_prid;
$mark = ' <<';
}
printf( "CPUID=%08x PRID=%04x%s\n", $pkg_cpuid, $pkg_prid, $mark );
}
}
print("\n");
for ( ; $fpos < $stat[7] ; ) {
$fpos += read( $fh, $buf, 8 );
my ( $upd_type, $upd_size ) = unpack( 'VV', $buf );
$fpos += read( $fh, $buf, $upd_size );
my (
$pat_date, $pat_pid, $pat_did, $pat_dlen, $pat_iflg,
$pat_dchk, $pat_ndid, $pat_sdid, $pat_prid
) = unpack( 'VVvCCVVVv', $buf );
my $mark = '';
if ( $prid && $pat_prid == $prid ) {
if ( $pat_pid == $ucode ) {
$mark = " UP-TO-DATE";
}
elsif ( $pat_pid > $ucode ) {
$mark = " UPDATE-AVAIL";
}
elsif ( $pat_pid < $ucode ) {
$mark = " OUTDATED";
}
}
printf( "PRID=%04x PID=%08x%s\n", $pat_prid, $pat_pid, $mark );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment