533a6a
#! /bin/bash -efu
1abd92
1abd92
# Generator of RPM "Provides:" tags for Intel microcode files.
1abd92
#
1abd92
# SPDX-License-Identifier: CC0-1.0
1abd92
1abd92
IFS=$'\n'
1abd92
UPDATED="intel-beta"
1abd92
CODENAMES="codenames"
1abd92
1abd92
if [ "$#" -ge 1 ]; then
1abd92
	CODENAMES="$1"
1abd92
	shift
1abd92
fi
1abd92
1abd92
# Match only FF-MM-SS ucode files under intel-ucode/intel-ucode-with-caveats
1abd92
# directories.
1abd92
for f in $(grep -E '/intel-ucode.*/[0-9a-f][0-9a-f]-[0-9a-f][0-9a-f]-[0-9a-f][0-9a-f]$'); do
1abd92
	ucode=$(basename "$f")
1abd92
	ucode_caveat="$(basename "$(dirname "$(dirname "$f")")")"
1abd92
	ucode_fname="$ucode_caveat/$ucode"
1abd92
	file_sz="$(stat -c "%s" "$f")"
1abd92
	skip=0
1abd92
1abd92
	while :; do
1abd92
		[ "$skip" -lt "$file_sz" ] || break
1abd92
1abd92
		# Microcode header format description:
1abd92
		# https://gitlab.com/iucode-tool/iucode-tool/blob/master/intel_microcode.c
1abd92
		IFS=' ' read hdrver rev \
1abd92
		       date_y date_d date_m \
1abd92
		       cpuid cksum ldrver \
1abd92
		       pf_mask datasz totalsz <<- EOF
1abd92
		$(dd if="$f" bs=1 skip="$skip" count=36 status=none \
1abd92
			| hexdump -e '"" 1/4 "%u " 1/4 "%#x " \
1abd92
			                 1/2 "%04x " 1/1 "%02x " 1/1 "%02x " \
1abd92
					 1/4 "%08x " 1/4 "%x " 1/4 "%#x " \
1abd92
					 1/4 "%u " 1/4 "%u " 1/4 "%u" "\n"')
1abd92
		EOF
1abd92
1abd92
		[ 0 != "$datasz" ] || datasz=2000
1abd92
		[ 0 != "$totalsz" ] || totalsz=2048
1abd92
1abd92
		# TODO: add some sanity/safety checks here.  As of now, there's
1abd92
		#       a (pretty fragile) assumption that all the matched files
1abd92
		#       are valid Intel microcode files in the expected format.
1abd92
1abd92
		skip=$((skip + totalsz))
1abd92
1abd92
		#[ -n "$rev" ] || continue
1abd92
1abd92
		# Basic "Provides:" tag. Everything else is bells and whistles.
1abd92
		# It's possible that microcode files for different platform_id's
1abd92
		# and the same CPUID have the same version, that's why "sort -u"
1abd92
		# in the end.
1abd92
		printf "firmware(intel-ucode/%s) = %s\n" "$ucode" "$rev"
1abd92
1abd92
		# Generate extended "Provides:" tags with additional
1abd92
		# information, which allow more precise matching.
1abd92
		printf "iucode_date(fname:%s;cpuid:%s;pf_mask:0x%x) = %s.%s.%s\n" \
1abd92
			"$ucode_fname" "$cpuid" "$pf_mask" "$date_y" "$date_m" "$date_d"
1abd92
		printf "iucode_rev(fname:%s;cpuid:%s;pf_mask:0x%x) = %s\n" \
1abd92
			"$ucode_fname" "$cpuid" "$pf_mask" "$rev"
1abd92
1abd92
		# Generate tags for each possible platform_id
1abd92
		_pf=1
1abd92
		_pf_mask="$pf_mask"
1abd92
		while [ 0 -lt "$_pf_mask" ]; do
1abd92
			[ 1 -ne "$((_pf_mask % 2))" ] || \
1abd92
				# We try to provide a more specific firmware()
1abd92
				# dependency here.  It has incorrect file name,
1abd92
				# but allows constructing a required RPM
1abd92
				# capability name by (directly) using
1abd92
				# the contents of /proc/cpuinfo and
1abd92
				# /sys/devices/system/cpu/cpu*/microcode/processor_flags
1abd92
				# (except for a Deschutes CPU with sig 0x1632)
1abd92
				printf "iucode_rev(fname:%s;platform_id:0x%x) = %s\n" \
1abd92
					"$ucode_fname" "$_pf" "$rev"
1abd92
1abd92
			_pf_mask=$((_pf_mask / 2))
1abd92
			_pf=$((_pf * 2))
1abd92
		done
1abd92
1abd92
		# Generate tags with codename information, in case
1abd92
		# it is available
1abd92
		cpuid_up="$(echo "$cpuid" | tr 'a-z' 'A-Z')"
1abd92
		if [ -e "$CODENAMES" ]; then
1abd92
			grep '	'"$cpuid_up"'	' "$CODENAMES" \
1abd92
			| while IFS=$'\t' read segm int_fname codename stepping candidate_pf rest; do
1abd92
				codename=$(echo "$codename" | tr ' (),' '_[];')
1abd92
				candidate_pf=$(printf "%u" "0x${candidate_pf}")
1abd92
				[ \( 0 -ne "$pf_mask" \) -a \
1abd92
				  \( "$candidate_pf" -ne "$((candidate_pf & pf_mask))" \) ] || { \
1abd92
					printf "iucode_rev(fname:%s;cpuid:%s;pf_mask:0x%x;segment:\"%s\";codename:\"%s\";stepping:\"%s\";pf_model:0x%x) = %s\n" \
1abd92
						"$ucode_fname" "$cpuid" "$pf_mask" \
1abd92
						"$segm" "$codename" "$stepping" "$candidate_pf" \
1abd92
						"$rev";
1abd92
					printf "iucode_date(fname:%s;cpuid:%s;pf_mask:0x%x;segment:\"%s\";codename:\"%s\";stepping:\"%s\";pf_model:0x%x) = %s.%s.%s\n" \
1abd92
						"$ucode_fname" "$cpuid" "$pf_mask" \
1abd92
						"$segm" "$codename" "$stepping" "$candidate_pf" \
1abd92
						"$date_y" "$date_m" "$date_d";
1abd92
				  }
1abd92
			done
1abd92
		fi
1abd92
1abd92
		# Kludge squared: generate additional "Provides:" tags
1abd92
		# for the files in the overrides tarball (that a placed
1abd92
		# in a separate caveat with a specific name)
1abd92
		[ "x${ucode_caveat}" != "x${UPDATED}" ] || {
1abd92
			printf "firmware_updated(intel-ucode/%s) = %s\n" \
1abd92
				"$ucode" "$rev";
1abd92
		}
1abd92
	done
1abd92
done | sort -u