xref: /illumos-gate/usr/src/data/ucode/update.amd (revision 873fefb258ee57f8d210b4709f2a8d8f034af869)
1#!/bin/ksh
2
3# This file and its contents are supplied under the terms of the
4# Common Development and Distribution License ("CDDL"), version 1.0.
5# You may only use this file in accordance with the terms of version
6# 1.0 of the CDDL.
7#
8# A full copy of the text of the CDDL should have accompanied this
9# source. A copy of the CDDL is also available via the Internet at
10# http://www.illumos.org/license/CDDL.
11
12# Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
13# Copyright 2025 Oxide Computer Company
14
15# A simple update script that checks out the upstream AMD firmware
16# repository, extracts the microcode into separate files within the amd/
17# directory, generates the required combined equivalence table and updates
18# the pkg(7) manifest.
19
20UPSTREAM=git://git.kernel.org
21UPSTREAM_PATH=/pub/scm/linux/kernel/git/firmware/linux-firmware.git
22
23# These are the GPG keys that AMD use to sign their CPU microcode updates
24# The first key is the current one and the second is one they have used for
25# older CPU families.
26GPGSERVER=keys.gnupg.net
27typeset -a GPGKEYS=(
28	0xFC7C6C505DAFCC14718357CAE4BE5339F328AE73
29	0x916A770823A7B27AADE01565A5E8DBC98C0108B4
30)
31
32function errexit {
33	echo "$@" >&2
34	exit 1
35}
36
37FW=platform/i86pc/ucode/AuthenticAMD
38
39export LC_ALL=C.UTF-8
40
41set -e
42set -o pipefail
43
44mf=../../pkg/manifests/system-microcode-amd.p5m
45[[ -f $mf ]] || errexit "Run from usr/src/data/ucode"
46
47function find_cmd {
48	typeset cmd="$1"
49	typeset var=$(echo $cmd | tr '[:lower:]' '[:upper:]')
50	typeset -n path="$var"
51	path=$(whence -fp "$cmd")
52	if (($? != 0)) || [ ! -x "$path" ]; then
53		errexit "Cannot find executable '$cmd' in PATH"
54	fi
55}
56
57# This script uses a few commands which are not part of illumos and are
58# expected to be available in the path.
59find_cmd git
60find_cmd gpg
61find_cmd pkgfmt
62find_cmd stat
63find_cmd xxd
64# Search for 'ucodeadm'. If you need to use an updated ucodeadm to handle this
65# firmware update, as is occasionally necessary, ensure it occurs earlier in
66# the path than /usr/sbin.
67find_cmd ucodeadm
68
69tmp=$(mktemp -d)
70[[ -n "$tmp" && -d "$tmp" ]]
71mkdir $tmp/out || errexit "Failed to create temporary directory"
72trap 'rm -rf $tmp' EXIT
73
74echo "** Adding AMD GPG signing keys to temporary keyring"
75mkdir -m 0700 $tmp/gnupg
76$GPG --homedir $tmp/gnupg --keyserver $GPGSERVER --receive-keys ${GPGKEYS[@]}
77
78echo "** Cloning $UPSTREAM$UPSTREAM_PATH"
79$GIT clone $UPSTREAM$UPSTREAM_PATH $tmp/ucode
80
81ver=`$GIT -C $tmp/ucode log -n1 --format=%ad --date=format:%Y%m%d amd-ucode`
82echo "** Updating to microcode version $ver"
83
84echo "** Verifying microcode signatures"
85for f in $tmp/ucode/amd-ucode/*.bin; do
86	if [[ ! -f "$f.asc" ]]; then
87		echo "Signature missing for ${f##*/}"
88		exit 1
89	fi
90	$GPG --homedir $tmp/gnupg --trust-model=always --verify $f{.asc,}
91done
92
93# Now that everything is in place and verified, begin modifying the tree.
94
95rm -f amd/*-*
96
97cp $tmp/ucode/LICENSE.amd-ucode amd/THIRDPARTYLICENSE
98echo AMD Processor Microcode Data Files > amd/THIRDPARTYLICENSE.descrip
99
100for f in $tmp/ucode/amd-ucode/*.bin; do
101	bf=${f##*/}
102	bf=${bf#microcode_}
103	bf=${bf%.bin}
104	[[ $bf = amd* ]] || errexit "$f does not look like a firmware file"
105	echo
106	echo "*** Converting $bf"
107	echo
108	mkdir $tmp/out/$bf
109	cp $f $tmp/amd-fw
110	$UCODEADM -l $tmp/amd-fw
111	$UCODEADM -i -R $tmp/out/$bf $tmp/amd-fw
112	rm -f $tmp/amd-fw
113done
114
115# Copy the firmware files into place
116cp $tmp/out/*/*-?? amd/
117
118# Deal with fallback firmware files (see amd/fallback/README)
119# For some processors AMD ship two microcode versions. One that can only be
120# loaded on systems which are already at a particular firmware level and an
121# older fallback one for systems that running old firmware. They package both
122# of these in the same microcode bundle with nothing to differentiate them
123# apart from their revision number. The older version occurs first in the
124# bundle. On illumos the newer version is always tried first and the older
125# version only used if that is not accepted by the processor. This script
126# looks for instances of more than one microcode for a CPU and moves the older
127# version into the amd/fallback directory. Anyone using this tool to prepare a
128# PR is expected to make judicious use of "git" and "ucodeadm" to verify that
129# everything looks reasonable afterwards.
130
131function ucode_version {
132	printf "0x%s" $($UCODEADM -t amd -l $1 | tr -s ' ' |
133	    awk -F'[ =]' '$4 == "Patch" {print $5}')
134}
135
136echo
137echo "** Relocating fallback microcode"
138echo
139
140for f in amd/*-01; do
141	bf=$(basename $f -01)
142	[[ -f "amd/$bf-00" ]] || errexit "$f has no -00 variant"
143	mv -f amd/$bf-00 amd/fallback/
144	mv amd/$bf-{01,00}
145	# Verify that the fallback version is lower than the main just in case
146	# AMD reverse the order of the patches for any reason.
147	typeset -i fbver=$(ucode_version amd/fallback/$bf-00)
148	typeset -i mlver=$(ucode_version amd/$bf-00)
149	printf "    %s - %X / %X\n" $bf $mlver $fbver
150	(( mlver > fbver )) || errexit "$bf - fallback is newer"
151done
152
153# Combine the equivalence tables from the different updates into one.
154# Each equivalence-table file is a sequence of 16-byte records with a
155# 16-byte terminator which is all zeros. To merge, we just concatenate
156# the unique non-terminator records and then add 16 bytes from /dev/zero.
157{
158	for f in $tmp/out/*/equivalence-table; do
159		size=$($STAT -c %s $f)
160		((size -= 16))
161		dd if=$f bs=1 count=$size status=none
162	done | $XXD -c16 | cut -d\  -f2- | sort -u | $XXD -r -p
163	# Add terminator
164	dd if=/dev/zero bs=1 count=16 status=none
165} > amd/equivalence-table
166
167$PKGFMT -u $mf
168mv $mf $mf.tmp
169egrep -v "file path=$FW" $mf.tmp > $mf
170rm -f $mf.tmp
171
172for f in amd/*-*; do
173	[[ -f $f ]] || continue
174	bf=${f##*/}
175	echo "file path=$FW/$bf group=sys mode=0444 reboot-needed=true" >> $mf
176done
177
178for f in amd/fallback/*-*; do
179	bf=${f##*/}
180	echo "file path=$FW/fallback/$bf"\
181           "group=sys mode=0444 reboot-needed=true" >> $mf
182done
183
184sed -i "/pkg.fmri.*microcode\/amd@/s/@[0-9]*/@$ver/" $mf
185
186$PKGFMT -fv2 $mf
187
188