<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>TinyComputers.io (Posts about ubuntu)</title><link>https://tinycomputers.io/</link><description></description><atom:link href="https://tinycomputers.io/categories/ubuntu.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Original site content © 2022–2026 Tiny Machines Workshop, LLC, except where otherwise noted. Some rights reserved.</copyright><lastBuildDate>Sun, 27 Sep 2026 20:52:08 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>The SpacemiT K3 on Mainline Ubuntu: KVM Works, and the BSDs Cannot Boot</title><link>https://tinycomputers.io/posts/spacemit-k3-ubuntu-kvm-follow-up.html?utm_source=feed&amp;utm_medium=rss&amp;utm_campaign=rss</link><dc:creator>A.C. Jokela</dc:creator><description>&lt;div class="audio-widget"&gt;
&lt;div class="audio-widget-header"&gt;
&lt;span class="audio-widget-icon"&gt;🎧&lt;/span&gt;
&lt;span class="audio-widget-label"&gt;Listen to this article&lt;/span&gt;
&lt;/div&gt;
&lt;audio controls preload="metadata"&gt;
&lt;source src="https://tinycomputers.io/spacemit-k3-ubuntu-kvm-follow-up_tts.mp3" type="audio/mpeg"&gt;
&lt;/source&gt;&lt;/audio&gt;
&lt;div class="audio-widget-footer"&gt;34 min · AI-generated narration&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;In &lt;a href="https://tinycomputers.io/posts/spacemit-k3-pico-itx-review.html"&gt;the first piece on this board&lt;/a&gt; I said the SpacemiT K3 Pico-ITX was genuinely competitive silicon shipped with a kernel that could not load KVM. I bought it to run virtual machines. It cost \$1,175.09 delivered and &lt;code&gt;modprobe kvm&lt;/code&gt; oopsed in &lt;code&gt;jump_label_module_notify&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you did not read that one, the short version: the K3 Pico-ITX is a RISC-V single board computer with 32 GB of RAM and sixteen cores, split into two groups of eight. Eight X100 cores do ordinary work. Eight A100 cores are an AI engine rated at 60 TOPS. It is the first RISC-V board I have tested that competes with ARM on speed rather than apologising for itself, and at \$1,175.09 it needs to.&lt;/p&gt;
&lt;p&gt;Two words will come up constantly here, so they are worth pinning down. The vendor kernel is the Linux that SpacemiT ships, which they maintain themselves and which knows every quirk of their hardware. Mainline is the Linux everyone else gets, where support has been accepted into the official kernel tree. Vendor kernels usually work better and age worse. The interesting moment for any new board is when mainline catches up, because that is when the board stops depending on one company's attention.&lt;/p&gt;
&lt;p&gt;Ubuntu 26.10 was the first release where this board's support is upstream rather than carried in SpacemiT's vendor tree. So I flashed EDK2 over the vendor U-Boot, installed Ubuntu, and re-ran everything.&lt;/p&gt;
&lt;p&gt;KVM works. That is the headline and it is a real result:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;modprobe&lt;span class="w"&gt; &lt;/span&gt;kvm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;lsmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;kvm
kvm&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;466944&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/kvm
crw-rw----&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;kvm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;232&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/kvm
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A Debian 13 riscv64 guest boots under &lt;code&gt;-cpu host&lt;/code&gt; and inherits the X100's vector and vector-crypto extensions. That &lt;code&gt;h&lt;/code&gt; on the end of the ISA string &lt;code&gt;rv64imafdcvh&lt;/code&gt; is the hypervisor extension, the optional block of silicon that lets a RISC-V chip run virtual machines at full speed instead of simulating them. The K3 has always had it. Until now no kernel on this board could use it. The board does the thing I bought it for.&lt;/p&gt;
&lt;p&gt;Everything else I found is worse than that sentence suggests. Including, first, the part where I could not install it.&lt;/p&gt;
&lt;h3&gt;Three failed installs and a serial cable&lt;/h3&gt;
&lt;p&gt;The supported path replaces SpacemiT's bootloader with EDK2, an open implementation of UEFI, the same firmware interface a PC uses to find and start an operating system. Swapping it in is what lets the board boot an ordinary Ubuntu USB stick instead of something hand-assembled for this hardware. The swap happens over fastboot, with the board held in a special flashing mode. The flash is the easy part. Hold the FDL button, apply power to the first USB-C connector, release, and &lt;code&gt;fastboot devices&lt;/code&gt; answers. Six partitions get written and &lt;code&gt;edk2.itb&lt;/code&gt; lands in the one confusingly named &lt;code&gt;uboot&lt;/code&gt;. That worked first time.&lt;/p&gt;
&lt;p&gt;The install did not. Three times the board booted the installer, appeared on the network as &lt;code&gt;ubuntu-server&lt;/code&gt;, and then sat there. For hours. No reboot, no progress, no way in. The DHCP lease renewed, ICMP answered, port 22 was open and refused every credential. From outside, a working install and a dead one look identical.&lt;/p&gt;
&lt;p&gt;I spent a long time constructing theories from that non-evidence. The storage match in my autoinstall config must be wrong. The kernel must not see the UFS. The firmware must have a dangling boot entry. Each theory was plausible, each led to another blind retry, and every one of them was wrong.&lt;/p&gt;
&lt;p&gt;What broke the deadlock was a &lt;a href="https://go.tinymachines.io/6v3F6F"&gt;three-wire USB-to-TTL serial adapter&lt;/a&gt; on the header silkscreened &lt;code&gt;DEBUG&lt;/code&gt;, next to the USB-C connectors. Any 3.3 V adapter works; mine is an older PL2303 and a CP2102 is the better buy today. Whatever you use, it must be 3.3 V TTL and not a true RS-232 cable, which swings to plus and minus twelve volts and will destroy the UART. The board's own device tree tells you where the console goes:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"serial0:115200n8"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;aliases&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;serial0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"/soc/serial@d4017000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Thirty seconds after attaching it, the answer was on screen. Two answers, actually.&lt;/p&gt;
&lt;p&gt;The first was the UFS error storm described below, saturating an 11.5 KB/s UART with 117 KB of messages every ten seconds. The second was better:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;Installer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;available&lt;/span&gt;
[&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;installer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;]
[&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Continue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;without&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;updating&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;]
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That prompt only appears when autoinstall is not active. Autoinstall is Ubuntu's unattended install mechanism: you leave a configuration file on the media, the installer reads it, and it answers every question for you without a human present. It is how you install a machine that has no screen. My configuration had never been applied on any attempt. Every one of those multi-hour "hangs" was the plain interactive installer, sitting at a prompt, waiting for a keypress from a machine with no keyboard and no screen.&lt;/p&gt;
&lt;p&gt;There is a second trap stacked on the first. Because the device tree pins the console to serial, &lt;a href="https://github.com/canonical/subiquity"&gt;subiquity&lt;/a&gt; starts in serial mode and opens with a dialog asking whether you want rich mode or basic mode. It waits. An unattended install on a board whose console is a UART will stop dead at that question, and nothing about it reaches the network.&lt;/p&gt;
&lt;p&gt;So I drove the installer over the serial line instead, one keystroke at a time, which is how Ubuntu actually got onto this board. It is worth knowing before you buy a headless RISC-V machine and assume autoinstall will carry you.&lt;/p&gt;
&lt;h3&gt;Mainline sees half the cores, and not the half you'd hope&lt;/h3&gt;
&lt;p&gt;Bianbu listed sixteen processors and let a process use one cluster of eight or the other, never both. I assumed a mainline kernel would either fix the partition or at least expose the same hardware.&lt;/p&gt;
&lt;p&gt;It does neither:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;possible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="n"&gt;online&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="n"&gt;uarch&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spacemit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x100&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="sr"&gt;/proc/s&lt;/span&gt;&lt;span class="n"&gt;et_ai_thread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;absent&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The A100 cluster is simply not enumerated. Bianbu at least showed all sixteen in &lt;code&gt;/proc/cpuinfo&lt;/code&gt; and gave you &lt;code&gt;/proc/set_ai_thread&lt;/code&gt; to reach the AI cores. Mainline does not know they exist. The vendor hook is gone with it.&lt;/p&gt;
&lt;p&gt;So the AI cores are less accessible on Ubuntu than on Bianbu, not more.&lt;/p&gt;
&lt;p&gt;The practical consequence is that the 110-second sixteen-core compile I speculated about in the first piece is not merely blocked, it is unreachable. There is no cluster to schedule onto. The A100 figure from that piece, 342.78 s against the X100's 161.29 s on the same tree, was measured through the vendor's &lt;code&gt;/proc/set_ai_thread&lt;/code&gt; hook. That hook does not exist on mainline, so on Ubuntu there is no way to run anything on those cores at all, fast or slow.&lt;/p&gt;
&lt;p&gt;One trap worth naming, because I fell into it and reported the wrong answer for a few minutes: &lt;code&gt;taskset -c 0-15&lt;/code&gt; succeeds here. It returns zero and looks like a win. It intersects the mask you asked for with the CPUs that exist and quietly gives you &lt;code&gt;0-7&lt;/code&gt;. Check the resulting affinity, not the exit code.&lt;/p&gt;
&lt;h3&gt;The compile got slower, and it is not the clock&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Bianbu 6.18.3 (vendor BSP)&lt;/th&gt;
&lt;th&gt;Ubuntu 7.3.0 (mainline)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rust compile, pinned tree, n=3&lt;/td&gt;
&lt;td&gt;161.80 s (sd 0.97)&lt;/td&gt;
&lt;td&gt;183.43 s (sd 2.12)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sysbench, 1 thread&lt;/td&gt;
&lt;td&gt;912.45 ev/s&lt;/td&gt;
&lt;td&gt;910.59 ev/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sysbench, 8 threads&lt;/td&gt;
&lt;td&gt;7,245.09 ev/s&lt;/td&gt;
&lt;td&gt;7,307.50 ev/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Same board, same tree at &lt;code&gt;2821bba&lt;/code&gt;, same rustc 1.98.1. Mainline is 13.4% slower on the compile.&lt;/p&gt;
&lt;p&gt;My first instinct was thermal throttling, because the three mainline runs climbed steadily (181.0, 184.2, 185.1) where Bianbu's were flat. That instinct was wrong. Single-threaded sysbench is a decent clock proxy and it is identical, within 0.2%. The eight-thread figure is actually 0.9% faster on mainline. Raw CPU throughput has not changed.&lt;/p&gt;
&lt;p&gt;So something other than the CPU costs 13% on a compile. The most likely candidate is storage: root moved from the internal UFS to NVMe behind LVM, and a clean &lt;code&gt;cargo build&lt;/code&gt; is I/O heavy. I have not proven it, and I am not going to claim a kernel regression I cannot demonstrate.&lt;/p&gt;
&lt;p&gt;Two things make this comparison less clean than I would like, and both are worth stating rather than burying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mainline exposes no cpufreq policy and no thermal zones for this SoC. On Bianbu the governor was &lt;code&gt;userspace&lt;/code&gt; pinned at 2.2 GHz and there were seven thermal zones. On Ubuntu the clock cannot be read, pinned, or checked for throttling.&lt;/li&gt;
&lt;li&gt;OpenSSL went from 3.5.5 to 4.0.1 between the two installs, so the crypto numbers are not comparable at all. I am not publishing a vector-crypto delta that is really a library version delta.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The internal storage does not work&lt;/h3&gt;
&lt;p&gt;The K3 has 119 GB of onboard UFS storage, which is the faster successor to the eMMC chips most single board computers solder down. On mainline it does not exist:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;ufshcd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;spacemit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c0e00000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;ufshc:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;specified&lt;/span&gt;
&lt;span class="n"&gt;ufshcd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;spacemit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c0e00000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;ufshc:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;ufshcd_populate_vreg:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Unable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vcc&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;supply&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regulator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assuming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;
&lt;span class="n"&gt;ufshcd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;spacemit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c0e00000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;ufshc:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;errors:&lt;/span&gt;&lt;span class="mh"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;DL_ERR:&lt;/span&gt;&lt;span class="mh"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x80000002&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mh"&gt;1600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;ufshcd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;spacemit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c0e00000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;ufshc:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ufshcd_async_scan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;failed:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mh"&gt;19&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The device tree is a data file the firmware hands the kernel describing what hardware is present and how it is wired: which controller sits at which address, which power rails feed it, what clock speeds it supports. Linux has no other way to discover any of this on a board like the K3. The K3's device tree declares the storage controller at &lt;code&gt;ufshc@c0e00000&lt;/code&gt; but supplies no &lt;code&gt;vcc&lt;/code&gt;/&lt;code&gt;vccq&lt;/code&gt; regulator entries and no &lt;code&gt;freq-table-hz&lt;/code&gt;. The driver assumes the rails are live, the device never answers, and link training fails at the Data Link and PHY Adapter layers until the controller gives up in &lt;code&gt;eh_fatal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The driver binds correctly, and the alias matches the board exactly (&lt;code&gt;of:N*T*Cspacemit,k3-ufshc&lt;/code&gt; against the DTB's &lt;code&gt;compatible = "spacemit,k3-ufshc"&lt;/code&gt;). The DTB is simply incomplete. Ubuntu ships and manages that DTB as a package, and I watched &lt;code&gt;update-initramfs&lt;/code&gt; print &lt;code&gt;Installing new k3-pico-itx.dtb&lt;/code&gt; while blacklisting the driver, so this is fixable upstream without new silicon and without a vendor release.&lt;/p&gt;
&lt;p&gt;Until then the internal storage is unusable and root has to live on NVMe. Mine is a &lt;a href="https://go.tinymachines.io/xtEZEf"&gt;WD_BLACK SN850X&lt;/a&gt;, which is overkill for a boot disk and was already in the board from the first review. I blacklisted &lt;code&gt;ufs_spacemit&lt;/code&gt;, which reclaims about forty seconds of boot and 1,600 error lines.&lt;/p&gt;
&lt;p&gt;That error storm is worth dwelling on, because it nearly cost me the whole install. The K3's device tree pins the kernel console to &lt;code&gt;serial0&lt;/code&gt; at 115200, which is about 11.5 KB/s. The UFS failure generates roughly 117 KB of error messages every ten seconds. The machine spends essentially all of its time writing errors to a UART whether or not anything is listening. From the outside it looks exactly like a hang.&lt;/p&gt;
&lt;h3&gt;The finding I did not expect&lt;/h3&gt;
&lt;p&gt;I wanted to see the board do real work, so I set out to build four RISC-V guests: Debian, FreeBSD, NetBSD and OpenBSD.&lt;/p&gt;
&lt;p&gt;Debian booted under KVM and ran fine. All three BSDs panicked about one second in.&lt;/p&gt;
&lt;p&gt;FreeBSD:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Illegal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc0002573&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xffffffc00042e362&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xffffffc00042e03e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arc4rand&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mh"&gt;0x8e&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;NetBSD, independently:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cpu_trap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fatal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trap&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;va&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mh"&gt;0xc0002773&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Those two values are the same instruction with a different destination register. Pulling &lt;code&gt;0xc0002573&lt;/code&gt; apart: the low seven bits are the opcode, &lt;code&gt;0x73&lt;/code&gt;, which on RISC-V means a system instruction. The middle field says it is a CSR read. The top twelve bits are the register number, &lt;code&gt;0xC00&lt;/code&gt;. That register is &lt;code&gt;cycle&lt;/code&gt;, a free-running count of CPU clock ticks, and the instruction that reads it is spelled &lt;code&gt;rdcycle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Both kernels were dying on the same one-instruction question: &lt;em&gt;how many cycles have elapsed?&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;A detour into who is allowed to do what&lt;/h4&gt;
&lt;p&gt;To explain why that question is illegal, I need one piece of RISC-V background, and it is the piece that makes everything else in this post make sense.&lt;/p&gt;
&lt;p&gt;RISC-V software runs at one of three privilege levels. Machine mode is the most privileged and is where firmware lives: on this board that is SpacemiT's OpenSBI, burned into flash, running before Linux and continuing to sit underneath it forever. Supervisor mode is where the operating system kernel runs. User mode is where your programs run. Each level can see and do strictly less than the one below it.&lt;/p&gt;
&lt;p&gt;Crucially, the lower level decides what the upper levels are permitted to touch. Machine-mode firmware holds a register called &lt;code&gt;mcounteren&lt;/code&gt;, the counter-enable register, with one bit per hardware counter. Set the bit and everyone above may read that counter. Leave it clear and any attempt to read it is an illegal instruction, exactly as if the CPU did not implement it at all.&lt;/p&gt;
&lt;p&gt;So &lt;code&gt;rdcycle&lt;/code&gt; failing is not a bug in the CPU, the kernel, or the hypervisor. It is firmware declining to grant permission, and there is nothing any layer above can do about it.&lt;/p&gt;
&lt;p&gt;My first theory was still the wrong one. I assumed RISC-V's KVM does not grant guests direct counter access, and that a guest is simply more restricted than its host. Wrong. The host cannot do it either:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;rdcycle.c
int&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;(&lt;/span&gt;void&lt;span class="o"&gt;){&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;unsigned&lt;span class="w"&gt; &lt;/span&gt;long&lt;span class="w"&gt; &lt;/span&gt;c&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;__asm__&lt;span class="w"&gt; &lt;/span&gt;volatile&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"rdcycle %0"&lt;/span&gt;:&lt;span class="s2"&gt;"=r"&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;./rdcycle
Illegal&lt;span class="w"&gt; &lt;/span&gt;instruction&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;core&lt;span class="w"&gt; &lt;/span&gt;dumped&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is plain userspace on the host, no virtualization anywhere. Testing all three counters:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CSR&lt;/th&gt;
&lt;th&gt;K3 host, U-mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rdtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rdcycle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SIGILL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rdinstret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SIGILL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The K3's M-mode firmware sets &lt;code&gt;mcounteren.TM&lt;/code&gt; but not &lt;code&gt;CY&lt;/code&gt; or &lt;code&gt;IR&lt;/code&gt;. Nothing above M-mode can read the cycle or instruction counters: not host userspace, not the host kernel, not any guest.&lt;/p&gt;
&lt;p&gt;FreeBSD's &lt;code&gt;arc4rand&lt;/code&gt; and NetBSD's early init both execute &lt;code&gt;rdcycle&lt;/code&gt;. So they panic on this board whether virtualized or not. This is not a KVM limitation and not a BSD bug. Linux is unaffected only because riscv64 Linux goes through SBI and the &lt;code&gt;time&lt;/code&gt; CSR instead of reading &lt;code&gt;cycle&lt;/code&gt; directly.&lt;/p&gt;
&lt;p&gt;I confirmed it the other way too. QEMU can run a guest two ways. KVM uses the host's own CPU to execute guest code directly, which is fast but means the guest inherits the host's hardware, firmware restrictions included. TCG ignores the hardware and simulates a RISC-V processor in software, which is slow but means the simulated CPU behaves the way the specification says rather than the way this board does. Under TCG, which emulates its own machine mode and its own counters, FreeBSD 15.1 and NetBSD 11.99.8 both boot to a root shell without complaint. The images are fine. The board is not.&lt;/p&gt;
&lt;p&gt;It is worth being precise about what this is not. NetBSD runs perfectly well on RISC-V: I have &lt;a href="https://tinycomputers.io/posts/four-partitions-and-a-borrowed-bootloader-netbsd-on-the-milk-v-mars.html"&gt;installed it on the Milk-V Mars&lt;/a&gt; and &lt;a href="https://tinycomputers.io/posts/two-addresses-one-page-finding-the-firmware-bug-behind-netbsd-corruption-on-the-milk-v-mars.html"&gt;chased a firmware bug through its page tables&lt;/a&gt; on that board. So I ran the same three-counter test on the Pine64 Star64, a JH7110 board sitting on the same bench:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CSR&lt;/th&gt;
&lt;th&gt;Star64 (JH7110)&lt;/th&gt;
&lt;th&gt;K3 Pico-ITX (SpacemiT K3)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rdtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rdcycle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;SIGILL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rdinstret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;SIGILL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Same operating systems, same architecture, same room. The JH7110's firmware grants all three counters and NetBSD boots on it. The K3's grants one. That is the whole difference, and it is a decision made in M-mode firmware rather than anything about RISC-V, NetBSD, or KVM.&lt;/p&gt;
&lt;p&gt;One practical note if you try this yourself: BSD guests need &lt;code&gt;virtio-blk-device&lt;/code&gt; (MMIO) rather than &lt;code&gt;if=virtio&lt;/code&gt;, which gives virtio-blk-PCI. NetBSD does not attach the PCI variant and drops to &lt;code&gt;root device:&lt;/code&gt; with only &lt;code&gt;vioif0&lt;/code&gt; offered.&lt;/p&gt;
&lt;h3&gt;What a guest actually gets&lt;/h3&gt;
&lt;p&gt;Since the whole point was virtualization, it is worth saying what running a guest on this board is actually like.&lt;/p&gt;
&lt;p&gt;The first thing that bites is firmware layering, and it is not documented anywhere obvious. Under KVM the host's OpenSBI already owns M-mode, so the guest must start in supervisor mode. Passing QEMU an M-mode firmware fails outright:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;qemu-system-riscv64: Machine mode firmware is not supported in combination with KVM.
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The invocation that works is &lt;code&gt;-bios none&lt;/code&gt; plus the S-mode U-Boot as the kernel, which then chainloads the guest's own bootloader over EFI. Under TCG the opposite is true: QEMU emulates the entire machine including M-mode, so OpenSBI has to be loaded with &lt;code&gt;-bios default&lt;/code&gt;. The same VM definition cannot serve both. I spent a while watching a TCG guest produce absolutely no output because I had carried the KVM arguments over unchanged, and with no M-mode firmware there was nothing to run the S-mode payload.&lt;/p&gt;
&lt;p&gt;What the guest sees is better than I expected. With &lt;code&gt;-cpu host&lt;/code&gt;, the Debian guest's ISA string comes through as:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;rv64imafdcv_zicbom_..._zvkned_zvknha_zvknhb_zvksed_zvksh_zvkt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Those last few are the vector crypto extensions, dedicated instructions for AES, SHA-2, SM4 and SM3. They are why this board pushes 2.37 GB/s of AES-GCM where an older RISC-V board manages 21.7 MB/s. The guest is not seeing a sanitised baseline RISC-V core, it is seeing the X100's actual feature set, which means the AES-GCM throughput that made this board interesting in the first place is available inside a VM. That is the part of this that genuinely works well.&lt;/p&gt;
&lt;p&gt;The guest ISA does not carry &lt;code&gt;h&lt;/code&gt;, so there is no nested virtualization. That is expected and I would not have used it.&lt;/p&gt;
&lt;p&gt;I cannot tell you much about how many guests this board holds, because I never found out. Three reached a shell: Debian under KVM, FreeBSD and NetBSD under TCG. OpenBSD got as far as extracting &lt;code&gt;base78.tgz&lt;/code&gt; and never finished, because by then it was clear that every BSD here was going to be an emulated guest and I stopped all three rather than let them grind. With those four QEMU processes up, one of which was only an installer, the host sat at load 2.07 and 5 GB of 31 GB. That is not a capacity test and I am not going to dress it up as one. It says only that the board was nowhere near its limits when I gave up on the experiment.&lt;/p&gt;
&lt;h3&gt;Which kernel should you actually run&lt;/h3&gt;
&lt;p&gt;That leaves a question the first piece could not ask, because there was only one option. Now there are two, and neither wins outright.&lt;/p&gt;
&lt;p&gt;Bianbu 4.0.4 with the vendor 6.18.3 BSP compiles 13.4% faster, drives the internal 119 GB UFS, exposes cpufreq so the clock can be pinned at 2.2 GHz, reports seven thermal zones, and enumerates all sixteen cores with a documented way to reach the A100 cluster. It cannot load KVM.&lt;/p&gt;
&lt;p&gt;Ubuntu 26.10 with mainline 7.3.0 loads KVM and runs accelerated guests. It cannot use the internal storage, shows no clock or temperature at all, and pretends eight of the cores do not exist.&lt;/p&gt;
&lt;p&gt;If you bought this board to compile things, the vendor kernel is still the better machine and it is not close. If you bought it to run VMs, which I did, mainline is the only option and you pay for it in storage, visibility and cores.&lt;/p&gt;
&lt;p&gt;What makes that trade-off worse than it sounds is that the losses are not symmetric. A slower compile is an inconvenience. Losing the internal storage means the NVMe slot is no longer an expansion option, it is a requirement, and the board's advertised 128 GB becomes decorative. Losing cpufreq and thermal zones means you cannot tell whether a long job is throttling. And losing the A100 cluster removes the 60 TOPS that justify a large part of the \$1,175.09.&lt;/p&gt;
&lt;p&gt;I keep coming back to that number. The &lt;a href="https://tinycomputers.io/posts/spacemit-k3-pico-itx-review.html"&gt;first piece&lt;/a&gt; argued the price was defensible because you get 32 GB, a hypervisor extension and an AI engine. Two of those three now work. The AI engine is reachable only from a kernel that cannot virtualize, which means on any given boot you can have the feature you paid for or the feature you paid for, but not both.&lt;/p&gt;
&lt;h3&gt;So where does that leave it&lt;/h3&gt;
&lt;p&gt;I stopped the BSDs. Emulating RISC-V on a RISC-V host defeats the entire point of buying hardware with the hypervisor extension, and a TCG guest on this board is slower than the same guest on a laptop. They were worth building only to find out why they would not run, and that turned out to be the most interesting thing I learned all week.&lt;/p&gt;
&lt;p&gt;The honest summary is narrower than "KVM works":&lt;/p&gt;
&lt;p&gt;KVM works, for Linux guests. The K3 is a capable Linux hypervisor with a lot of memory. It is also, right now, a hostile host for the BSDs at the firmware level, it cannot use its own internal storage on a mainline kernel, and it hides half its cores from the only kernel that can virtualize properly.&lt;/p&gt;
&lt;p&gt;Every one of those is a software or firmware problem rather than a silicon one, which is the same conclusion I reached the first time. The difference is that the list is now specific enough to fix: grant &lt;code&gt;CY&lt;/code&gt; and &lt;code&gt;IR&lt;/code&gt; in &lt;code&gt;mcounteren&lt;/code&gt;, fill in the UFS regulators in the device tree, and describe the A100 cluster upstream.&lt;/p&gt;
&lt;h3&gt;One more piece to come&lt;/h3&gt;
&lt;p&gt;Two and a half weeks before this board arrived, I &lt;a href="https://tinycomputers.io/posts/would-you-pay-for-risc-v.html"&gt;costed a RISC-V rental business in public&lt;/a&gt;: two K3 Pico-ITX nodes in a 1U at a Miami colo, roughly \$47 to \$57 per node per month, and a question to readers about whether anyone would actually pay for it. The central pitch was that the hypervisor extension is the one thing you cannot rent from Scaleway or anybody else today.&lt;/p&gt;
&lt;p&gt;The best news is that the pitch survives. KVM works, so the differentiator is real rather than theoretical, and the estimate I published without owning the hardware turned out to describe the right machine. The design held up better than I did, too: that plan already budgeted a Raspberry Pi as the BMC holding a USB-UART console to each board, and NVMe drives at about \$120 the pair. Both of those turned out to be exactly the things this week proved you cannot run this board without.&lt;/p&gt;
&lt;p&gt;What changes is subtler and mostly unflattering. Renting virtual machines forces the mainline kernel, and on mainline the 128 GB of onboard storage in that \$399 board specification is dead weight, because the UFS controller never links. You are paying for storage you cannot use. The BSD finding removes a customer segment that a RISC-V rental should have been well placed to serve. And mainline reports no clock and no temperature at all, which means a tenant asking whether their node is throttling gets an honest shrug. A BMC can power-cycle a wedged board, but it cannot report numbers the kernel refuses to collect.&lt;/p&gt;
&lt;p&gt;There is one operational gap worth designing around. The BMC switches 12 V and watches the console, which covers a hung node. It does not cover firmware recovery, because entering flash mode on this board means holding a physical button while power is applied. That is a GPIO line and a bit of wiring rather than a flight to Miami, but it is not in the current design and I only know it matters because I spent this week needing it.&lt;/p&gt;
&lt;p&gt;I want a few more weeks of uptime before I redo those numbers. The next piece will be that arithmetic revisited by someone who now owns the hardware instead of estimating it.&lt;/p&gt;
&lt;p&gt;Caveats. The compile comparison has an unresolved 13% gap with a confirmed-identical CPU throughput on both sides; root storage differs between the two installs and is the leading suspect. Clocks could not be pinned or verified on mainline because no cpufreq policy is exposed. Crypto figures are not compared across installs because the OpenSSL version changed. The A100 cluster was not benchmarked because mainline does not enumerate it. The virtualization experiment was abandoned rather than completed: three guests reached a shell, OpenBSD's install was stopped part-way, and no attempt was made to find out how many guests the board will hold. All raw data is in the &lt;a href="https://github.com/ajokela/benchmark-sbcs"&gt;benchmark repository&lt;/a&gt;.&lt;/p&gt;</description><category>benchmarks</category><category>bianbu</category><category>bsd</category><category>firmware</category><category>freebsd</category><category>hardware review</category><category>hypervisor extension</category><category>k3</category><category>kvm</category><category>netbsd</category><category>openbsd</category><category>pico-itx</category><category>qemu</category><category>risc v</category><category>riscv64</category><category>rva23</category><category>single board computers</category><category>spacemit</category><category>spacemit a100</category><category>spacemit x100</category><category>ubuntu</category><category>virtualization</category><guid>https://tinycomputers.io/posts/spacemit-k3-ubuntu-kvm-follow-up.html</guid><pubDate>Sun, 27 Sep 2026 13:00:00 GMT</pubDate></item><item><title>The SpacemiT K3: RISC-V Finally Competes, and the Vendor Kernel Gets in the Way</title><link>https://tinycomputers.io/posts/spacemit-k3-pico-itx-review.html?utm_source=feed&amp;utm_medium=rss&amp;utm_campaign=rss</link><dc:creator>A.C. Jokela</dc:creator><description>&lt;div class="audio-widget"&gt;
&lt;div class="audio-widget-header"&gt;
&lt;span class="audio-widget-icon"&gt;🎧&lt;/span&gt;
&lt;span class="audio-widget-label"&gt;Listen to this article&lt;/span&gt;
&lt;/div&gt;
&lt;audio controls preload="metadata"&gt;
&lt;source src="https://tinycomputers.io/spacemit-k3-pico-itx-review_tts.mp3" type="audio/mpeg"&gt;
&lt;/source&gt;&lt;/audio&gt;
&lt;div class="audio-widget-footer"&gt;29 min · AI-generated narration&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Every RISC-V board I have reviewed here has needed an excuse. The &lt;a href="https://tinycomputers.io/posts/pine64-star64-riscv-review.html"&gt;Pine64 Star64&lt;/a&gt; compiles Rust slower than a quad Cortex-A53 from 2012. The &lt;a href="https://tinycomputers.io/posts/mangopi-mq-pro-riscv-review.html"&gt;MangoPi MQ Pro&lt;/a&gt; takes two hours and ten minutes to do what a Raspberry Pi 5 does in seventy-seven seconds. The &lt;a href="https://tinycomputers.io/posts/the-orangepi-rv2.html"&gt;Orange Pi RV2&lt;/a&gt; spends eight cores losing to four. The reviews all end the same way: buy it because it is RISC-V, not because it is good.&lt;/p&gt;
&lt;p&gt;The SpacemiT K3 Pico-ITX does not need that excuse. It compiles the benchmark in 161.80 seconds, which puts it seventh of thirteen boards on my bench and ahead of the Banana Pi CM5-Pro. It pushes 2,372 MB/s of AES-128-GCM, beating the Cortex-A76 in a Rockchip RK3588. On SHA-512 it is 1.72x faster than that A76. A single SpacemiT X100 core scores 94% of an A76 on sysbench.&lt;/p&gt;
&lt;p&gt;This is the first RISC-V machine I have tested that competes on the merits.&lt;/p&gt;
&lt;p&gt;It also cost \$1,175.09 delivered, cannot schedule a single job across more than 8 of its 16 cores, and cannot run a virtual machine at all, which is the specific thing I bought it for.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The SpacemiT K3 retail box, black with white lettering reading RISC-V AI CPU K3, RVA23 Profile Chip, with the SpacemiT logo on the kraft sleeve behind it" src="https://tinycomputers.io/images/spacemit-k3/box-rva23.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;"RVA23 Profile Chip" on the box is the part that matters. RVA23 is the RISC-V application profile that mandates the vector extension and the vector crypto suite, which is exactly why this board's ISA string is so much richer than every other RISC-V machine on my bench.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;What it cost&lt;/h3&gt;
&lt;p&gt;I am putting this near the top because every other review of frontier RISC-V hardware buries it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Cost breakdown: board \$889.00, shipping \$45.00, customs duty \$241.09, total \$1,175.09" src="https://tinycomputers.io/images/spacemit-k3/cost-breakdown.png"&gt;&lt;/p&gt;
&lt;p&gt;The board was \$889 for the 32 GB RAM and 128 GB storage configuration, \$45 shipping, and then \$241.09 in customs duty collected by DHL four days later. That duty is 27.1% on top of the order and appears on no spec sheet or product page.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The shipping box as it arrived, wrapped in plastic with two DHL Express Worldwide waybills, origin Hong Kong, recipient details blacked out" src="https://tinycomputers.io/images/spacemit-k3/package-dhl.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Origin HKG, shipped by Firefly Technology out of Tsing Yi. Declared contents: "plastic main board for to drive display", 0.85 kg. That declaration is what the \$241.09 got assessed against.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A &lt;a href="https://go.tinymachines.io/cxBdNQ"&gt;Raspberry Pi 5 with 8 GB&lt;/a&gt; currently sells for around \$200 on Amazon, not the \$80 I quoted in older reviews here. So the honest multiple is roughly 5.9x the price for a board with four times the RAM, twice the cores on paper, vector crypto, PCIe, an NVMe slot and a hypervisor extension. It is not a \$1,095 difference in value, but it is also not the 15x gouging that a stale MSRP comparison would suggest.&lt;/p&gt;
&lt;h3&gt;The hardware&lt;/h3&gt;
&lt;p&gt;&lt;img alt="SpacemiT K3 Pico-ITX seen from above under a clear acrylic cover, showing the SpacemiT logo silkscreen, a yellow coin-cell RTC battery, two M.2 slots, a SIM slot and an eDP connector" src="https://tinycomputers.io/images/spacemit-k3/board-top.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Pico-ITX, under the acrylic cover it ships with. Two M.2 slots, a coin-cell RTC, an eDP header, and MAC address stickers on the right.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Port edge of the SpacemiT K3 showing two USB-C connectors, four USB-A ports in two stacks, a gigabit Ethernet jack and an SFP cage" src="https://tinycomputers.io/images/spacemit-k3/board-ports.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The port edge: two USB-C, four USB-A, gigabit Ethernet, and an SFP cage on the right. Only the gigabit port had a link during testing.&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Specification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SoC&lt;/td&gt;
&lt;td&gt;SpacemiT K3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;16 cores enumerated: 8x Spacemit X100 @ 2.2 GHz, 8x Spacemit A100 @ 1.8 GHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rv64imafdcvh&lt;/code&gt; plus Zba/Zbb/Zbc/Zbs, V (vector, &lt;code&gt;vlen:256&lt;/code&gt;), H (hypervisor)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector crypto&lt;/td&gt;
&lt;td&gt;Zvkned, Zvknha, Zvknhb, Zvksed, Zvksh, Zvkg, Zvbb, Zvbc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;32 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;119 GB onboard UFS, plus an NVMe slot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;PowerVR B-Series BXM-4-64 MC1, DRM nodes present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;Gigabit, link up on &lt;code&gt;end0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS as shipped&lt;/td&gt;
&lt;td&gt;Bianbu 4.0.4, kernel 6.18.3-generic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That ISA string is the most complete of any RISC-V board I have tested, and by a wide margin. Every previous one was &lt;code&gt;rv64imafdc&lt;/code&gt; with nothing above baseline. This one has vectors, bit manipulation, the full vector crypto suite, and the hypervisor extension.&lt;/p&gt;
&lt;p&gt;I parsed it with the &lt;code&gt;rv64&lt;/code&gt; prefix stripped before matching single letters, because a naive substring search finds the &lt;code&gt;v&lt;/code&gt; in &lt;code&gt;rv64&lt;/code&gt; and reports a vector unit that is not there. That mistake cost me an hour on the Star64.&lt;/p&gt;
&lt;p&gt;&lt;img alt="fastfetch output on the K3 showing Bianbu 4.0.4 riscv64, kernel 6.18.3-generic, 16 cores at 2.20 GHz, 31.31 GiB of RAM, a PowerVR B-Series BXM-4-64 GPU and a 1.79 TiB NVMe mounted at /srv" src="https://tinycomputers.io/images/spacemit-k3/fastfetch.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;As shipped, plus the NVMe I added. Note the sixteen cores it reports, and hold that thought.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Compiling Rust&lt;/h3&gt;
&lt;p&gt;The workload is a clean release build of &lt;a href="https://github.com/ajokela/ballistics-engine"&gt;ballistics-engine&lt;/a&gt;, three runs after &lt;code&gt;cargo clean&lt;/code&gt;, in two tiers: the pinned tree at commit &lt;code&gt;2821bba&lt;/code&gt; that every archived fleet result used, and current &lt;code&gt;HEAD&lt;/code&gt; at &lt;code&gt;b0cfcd4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pinned tier (&lt;code&gt;2821bba&lt;/code&gt;, 16,639 LOC, 144 crates):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;160.68 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;162.41 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;162.30 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean&lt;/td&gt;
&lt;td&gt;161.80 s (sigma 0.97, 0.60% CV)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;HEAD tier (&lt;code&gt;b0cfcd4&lt;/code&gt;, 155,867 LOC, 243 crates):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1271.27 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1273.18 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1271.06 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean&lt;/td&gt;
&lt;td&gt;1271.84 s (sigma 1.16, 0.09% CV)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A 0.09% coefficient of variation across three twenty-one minute builds is the tightest result on my bench. Nothing on this board wobbles: the governor is &lt;code&gt;userspace&lt;/code&gt; pinned at maximum, both clusters hold their clocks, and there is no thermal headroom being negotiated.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Two-panel benchmark chart. Left: Rust compile times across thirteen boards, with the SpacemiT K3's X100 cluster at 161.8 s in seventh place and its A100 cluster shown separately as a hatched bar at 342.8 s. Right: single-thread crypto throughput for the K3, a Cortex-A76 and a Pine64 Star64 across four algorithms" src="https://tinycomputers.io/images/spacemit-k3-benchmarks.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Left: the pinned-tree fleet. The K3 appears twice because its two clusters are, in practice, two separate eight-core machines. The &lt;a href="https://go.tinymachines.io/mQAZZF"&gt;MangoPi MQ Pro&lt;/a&gt; is omitted at 7,824 s, which is 48x the next slowest board and would flatten the chart. Right: single-thread crypto throughput, linear scale, three platforms.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The generational comparison is the cleanest number here. The &lt;a href="https://go.tinymachines.io/453BQB"&gt;Orange Pi RV2&lt;/a&gt; runs SpacemiT's previous Ky X1 and takes 650.60 s on the same tree with the same rustc 1.98.1. The K3 does it in 161.80. That is 4.02x faster from the same vendor, one generation later.&lt;/p&gt;
&lt;p&gt;Against ARM it is a tie rather than a win. 161.80 s versus the &lt;a href="https://go.tinymachines.io/GNVHU7"&gt;Banana Pi CM5-Pro&lt;/a&gt;'s 167.15 s is a 3% margin, which I would not defend as a victory. But a RISC-V board landing within noise of an eight-core ARM part is new.&lt;/p&gt;
&lt;h3&gt;Cryptography, where it actually wins&lt;/h3&gt;
&lt;p&gt;Single-thread OpenSSL 3.5.5, 16 KB blocks:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;SpacemiT K3&lt;/th&gt;
&lt;th&gt;RK3588 Cortex-A76&lt;/th&gt;
&lt;th&gt;Pine64 Star64&lt;/th&gt;
&lt;th&gt;vs A76&lt;/th&gt;
&lt;th&gt;vs Star64&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AES-128-GCM&lt;/td&gt;
&lt;td&gt;2372.7&lt;/td&gt;
&lt;td&gt;2185.7&lt;/td&gt;
&lt;td&gt;21.7&lt;/td&gt;
&lt;td&gt;1.09x&lt;/td&gt;
&lt;td&gt;109x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-256-GCM&lt;/td&gt;
&lt;td&gt;1853.0&lt;/td&gt;
&lt;td&gt;1763.9&lt;/td&gt;
&lt;td&gt;17.9&lt;/td&gt;
&lt;td&gt;1.05x&lt;/td&gt;
&lt;td&gt;104x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-256&lt;/td&gt;
&lt;td&gt;1520.6&lt;/td&gt;
&lt;td&gt;1429.4&lt;/td&gt;
&lt;td&gt;35.4&lt;/td&gt;
&lt;td&gt;1.06x&lt;/td&gt;
&lt;td&gt;43x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-512&lt;/td&gt;
&lt;td&gt;646.8&lt;/td&gt;
&lt;td&gt;376.1&lt;/td&gt;
&lt;td&gt;63.5&lt;/td&gt;
&lt;td&gt;1.72x&lt;/td&gt;
&lt;td&gt;10x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-128-CBC&lt;/td&gt;
&lt;td&gt;1622.7&lt;/td&gt;
&lt;td&gt;1831.8&lt;/td&gt;
&lt;td&gt;31.4&lt;/td&gt;
&lt;td&gt;0.89x&lt;/td&gt;
&lt;td&gt;52x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;372.4&lt;/td&gt;
&lt;td&gt;674.4&lt;/td&gt;
&lt;td&gt;51.0&lt;/td&gt;
&lt;td&gt;0.55x&lt;/td&gt;
&lt;td&gt;7x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A RISC-V board beating a Cortex-A76, the one in an &lt;a href="https://go.tinymachines.io/wvZnMf"&gt;Orange Pi 5 Max&lt;/a&gt;, on AES-GCM, SHA-256 and SHA-512 has not happened before on this bench.&lt;/p&gt;
&lt;p&gt;Isolating the extensions makes the mechanism obvious. Restricting &lt;code&gt;OPENSSL_riscvcap&lt;/code&gt; to plain &lt;code&gt;rv64gc&lt;/code&gt; disables the vector crypto fast paths:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Full detection&lt;/th&gt;
&lt;th&gt;&lt;code&gt;rv64gc&lt;/code&gt; only&lt;/th&gt;
&lt;th&gt;Gain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AES-128-GCM&lt;/td&gt;
&lt;td&gt;2373.3 MB/s&lt;/td&gt;
&lt;td&gt;68.8 MB/s&lt;/td&gt;
&lt;td&gt;34.5x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-256-GCM&lt;/td&gt;
&lt;td&gt;1854.3 MB/s&lt;/td&gt;
&lt;td&gt;56.7 MB/s&lt;/td&gt;
&lt;td&gt;32.7x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-256&lt;/td&gt;
&lt;td&gt;1520.0 MB/s&lt;/td&gt;
&lt;td&gt;146.1 MB/s&lt;/td&gt;
&lt;td&gt;10.4x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-512&lt;/td&gt;
&lt;td&gt;646.8 MB/s&lt;/td&gt;
&lt;td&gt;270.6 MB/s&lt;/td&gt;
&lt;td&gt;2.4x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Strip the extensions and this board performs like every other RISC-V machine I have tested. That 68.8 MB/s figure is in the same league as the Star64's 21.7. The silicon is not what changed; the instruction set is.&lt;/p&gt;
&lt;p&gt;The two losses are informative rather than embarrassing. Zvkned and Zvknh accelerate AES and SHA specifically, so ChaCha20 gets nothing from them and falls behind a chip whose general SIMD is better tuned in OpenSSL. If you are terminating TLS on this board, prefer AES-GCM, which is the opposite of the advice I gave for the Star64.&lt;/p&gt;
&lt;p&gt;Asymmetric work is unremarkable: RSA-2048 at 245 signs/s, ECDSA P-256 at 4,437 signs/s. Nobody accelerates those in silicon here.&lt;/p&gt;
&lt;h3&gt;You get eight cores at a time, never sixteen&lt;/h3&gt;
&lt;p&gt;This is the finding that changes how you should read every number above. I got it wrong twice before I got it right, so here is the whole path.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/proc/cpuinfo&lt;/code&gt; lists sixteen processors. Eight report &lt;code&gt;model name: Spacemit(R) X100&lt;/code&gt; and eight report &lt;code&gt;Spacemit(R) A100&lt;/code&gt;, with distinct &lt;code&gt;marchid&lt;/code&gt; and &lt;code&gt;mimpid&lt;/code&gt; values. The boot log says &lt;code&gt;smp: Brought up 1 node, 16 CPUs&lt;/code&gt;. The &lt;code&gt;possible&lt;/code&gt;, &lt;code&gt;present&lt;/code&gt; and &lt;code&gt;online&lt;/code&gt; masks all read &lt;code&gt;0-15&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And then:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;nproc&lt;/span&gt;&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;PID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cpus_allowed_list&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="n"&gt;taskset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;anything&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;affinity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;
&lt;span class="n"&gt;cpu8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jiffies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;burn&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;cpu0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jiffies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;burn&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My first reading was that the vendor kernel was hiding half the machine. My second reading, after actually looking up what an A100 is, was that the A100 cluster is a 60 TOPS AI engine that merely happens to be built from RISC-V harts, so &lt;code&gt;nproc&lt;/code&gt; returning 8 is correct and there is nothing to complain about.&lt;/p&gt;
&lt;p&gt;Both readings are wrong, and the truth is more annoying than either.&lt;/p&gt;
&lt;p&gt;The A100 cores are reachable. You write a PID to &lt;code&gt;/proc/set_ai_thread&lt;/code&gt;, which is world-writable, and that thread's affinity mask flips from &lt;code&gt;0-7&lt;/code&gt; to &lt;code&gt;8-15&lt;/code&gt;. The setting is inherited by children, so it is enough to do it once in a shell and then run whatever you like. &lt;code&gt;taskset&lt;/code&gt; works normally afterwards, within the new mask. That is why plain &lt;code&gt;taskset -c 8&lt;/code&gt; fails as root: the affinity mask is not the door, but there is a door.&lt;/p&gt;
&lt;p&gt;And once you are through it, the A100 cores run ordinary code perfectly well. Not AI kernels. Not vector math. &lt;code&gt;rustc&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;Rust clean release build, pinned tree 2821bba, cargo -j8, n=2:

  X100 cluster (cpu0-7,  2.2 GHz) : 161.58 s, 161.00 s   mean 161.29 s
  A100 cluster (cpu8-15, 1.8 GHz) : 344.32 s, 341.23 s   mean 342.78 s
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The A100 cluster compiles the same tree 2.13x slower than the X100 cluster. Correct for the 2.2 versus 1.8 GHz clock difference and the A100 cores land at 57.5% of X100 per-clock throughput. sysbench, a completely different workload, puts the same figure at 57.4% (428 events/s against 912 single-threaded, 3,430 against 7,245 across eight). Two unrelated benchmarks agreeing to a tenth of a point is about as solid as this kind of measurement gets.&lt;/p&gt;
&lt;p&gt;So these are not accelerator lanes that cannot run general code. They are eight real, if unexceptional, RISC-V application cores, roughly 57% the per-clock performance of the big ones, that SpacemiT has tuned for AI work by giving them 1024-bit vectors instead of the X100's 256-bit.&lt;/p&gt;
&lt;p&gt;Here is the actual problem. You cannot have both clusters at once. After opting in, &lt;code&gt;taskset -pc 0-15&lt;/code&gt; fails:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;taskset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;56055&lt;/span&gt;&lt;span class="s1"&gt;'s affinity: Invalid argument&lt;/span&gt;
&lt;span class="s1"&gt;pid 56055'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;affinity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;0-7&lt;/code&gt; or &lt;code&gt;8-15&lt;/code&gt;. Never &lt;code&gt;0-15&lt;/code&gt;. No process on this machine can be scheduled across all sixteen cores, so no single job can use the whole chip. That is why the compile pinned to &lt;code&gt;cpu0-7&lt;/code&gt; finished in 161.14 s against 161.80 s for the nominally sixteen-core run: the nominally sixteen-core run was always an eight-core run, because sixteen-core runs do not exist here.&lt;/p&gt;
&lt;p&gt;What that costs is easy to bound. Summing the two clusters' throughput puts a hypothetical combined build at about 110 seconds, roughly 1.47x faster than the X100 cluster alone. Treat that as a ceiling rather than a prediction, because this build's serialised LTO link would eat into it and cross-cluster scheduling on a chip this asymmetric is not free. But the gap between 161 s and something near 110 s is real compute, sitting in the same coherence domain, fenced off by software.&lt;/p&gt;
&lt;p&gt;I am not sure whether the fence is deliberate product design, a limitation of the vendor scheduler patches, or a hardware constraint I cannot see from userspace. SpacemiT's marketing splits the chip into "130K DMIPS of general-purpose compute and 60 TOPS of AI compute", which suggests they think of the two clusters as separate products that happen to share a die. That is a defensible way to build a chip. It is a poor way to describe one, because nothing on the board, in &lt;code&gt;fastfetch&lt;/code&gt;, or in the shipped OS tells you any of this. You find out by pinning a compile to &lt;code&gt;cpu8-15&lt;/code&gt; and getting a 0.016 second result.&lt;/p&gt;
&lt;p&gt;The one A100 claim I still cannot check is the interesting one: SpacemiT's IME2 llama.cpp build reportedly puts the A100 cluster ahead of X100 on token generation, which is the entire point of those 1024-bit vectors. That needs their runtime and a proper model, and it belongs in its own piece.&lt;/p&gt;
&lt;p&gt;Geekbench makes the same mistake I made first, which is some comfort. I uploaded a run as &lt;a href="https://browser.geekbench.com/v7/cpu/466791"&gt;Geekbench 7 result 466791&lt;/a&gt;: 363 single-core, 2019 multi-core under Geekbench 7.0.0 Preview for Linux RISC-V. Its system panel reports "1 Processor, 16 Cores", labels the whole chip &lt;code&gt;Spacemit(R) X100&lt;/code&gt;, and then gives up with "Cluster 1: 0 Cores". That is detection logic meeting a topology it has no model for.&lt;/p&gt;
&lt;p&gt;The scores contradict the topology it printed. Multi-core comes in at 5.56x single-core, and the Clang subtest at 6.43x. Those are eight-core scaling factors, measured on the eight cores Geekbench was allowed to touch.&lt;/p&gt;
&lt;h3&gt;The benchmark's own blind spot&lt;/h3&gt;
&lt;p&gt;While measuring this I found a flaw in my own workload that applies to every board I have tested.&lt;/p&gt;
&lt;p&gt;The project's release profile sets &lt;code&gt;lto = true&lt;/code&gt; and &lt;code&gt;codegen-units = 1&lt;/code&gt;, which forces the final crate through a single-threaded link. Parallel efficiency on the K3 falls from 46% on the pinned tree to 22% on HEAD, because HEAD's final crate is much larger and the serial portion grows faster than the parallel one.&lt;/p&gt;
&lt;p&gt;That penalises wide machines. A four-core board loses little to a serial link; an eight or sixteen-core board idles through it. Every board ran the identical profile so the fleet numbers remain comparable to each other, but they measure "compile this specific LTO-heavy project" rather than general compile throughput. The K3 is the first machine wide enough to make that visible, and I should have caught it sooner.&lt;/p&gt;
&lt;h3&gt;Thermals, memory, storage&lt;/h3&gt;
&lt;p&gt;Nothing dramatic, which is itself worth reporting.&lt;/p&gt;
&lt;p&gt;Seven thermal zones sit at 51 to 56 C idle. Under 120 seconds of sixteen-worker &lt;code&gt;stress-ng&lt;/code&gt; the hottest cluster peaked at 65 C and both clusters held full clock throughout. No throttling.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Underside of the SpacemiT K3 showing a large aluminium heatsink with an integrated blower fan bolted over the board" src="https://tinycomputers.io/images/spacemit-k3/board-cooler.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The reason the thermal numbers look good: an aluminium heatsink with an integrated blower fan, bolted to the board. No other RISC-V board in this comparison ships with active cooling.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Worth being precise about why: this board is actively cooled. It ships with a substantial aluminium heatsink and a blower fan bolted to the underside, which is not something any other RISC-V board in this comparison has. Holding 65 C under full load is a good result, but it is a good result with a fan, not in spite of having none.&lt;/p&gt;
&lt;p&gt;Memory measured 32,468 MB/s read and 23,768 MB/s write. The onboard UFS does 903 MB/s buffered read via hdparm, 238 MB/s write and 932 MB/s read by &lt;code&gt;dd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also fitted a &lt;a href="https://go.tinymachines.io/xtEZEf"&gt;WD_BLACK SN850X 2 TB&lt;/a&gt; in the NVMe slot, which does 1.0 GB/s write and 2.1 GB/s read. That is a different universe from the microSD-bound RISC-V boards I have reviewed, where the Star64 managed 22.4 MB/s because its controller never leaves SD high-speed mode.&lt;/p&gt;
&lt;h3&gt;KVM is broken&lt;/h3&gt;
&lt;p&gt;I bought this board to run virtual machines. Thirty-two gigabytes of RAM and a hypervisor extension is a credible small virtualization host, and RISC-V finally having RVH 1.0 with AIA and an IOMMU is the reason to care about the K3 over anything else on this list.&lt;/p&gt;
&lt;p&gt;It does not work.&lt;/p&gt;
&lt;p&gt;The ISA string advertises &lt;code&gt;h&lt;/code&gt;. The kernel ships &lt;code&gt;CONFIG_KVM=m&lt;/code&gt;. Loading the module produces a kernel oops:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;Unable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;paging&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;virtual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ffffffff81a0f1c0&lt;/span&gt;
&lt;span class="n"&gt;Oops&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#1]&lt;/span&gt;
&lt;span class="n"&gt;CPU&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;55989&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Comm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modprobe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tainted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;generic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#1.0.5.4&lt;/span&gt;
&lt;span class="n"&gt;Hardware&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SpacemiT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;K3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pico&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;epc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jump_label_module_notify&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mh"&gt;0x1de&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mh"&gt;0x368&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;load_module&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mh"&gt;0x15d4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blocking_notifier_call_chain_robust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifier_call_chain&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The fault is in the static-key relocation path during module load, so &lt;code&gt;/dev/kvm&lt;/code&gt; is never created and no virtual machine can start. This is a software defect in Bianbu's kernel build, not a hardware limitation: SpacemiT's own materials describe RVH 1.0, AIA and IOMMU support, and K3 KVM work has been landing upstream.&lt;/p&gt;
&lt;p&gt;Do not buy this board expecting to run VMs on the image it ships with.&lt;/p&gt;
&lt;h3&gt;Who this is for&lt;/h3&gt;
&lt;p&gt;Buy it if you are doing RISC-V platform work and need real hardware that does not make you wait: a build machine, a CI runner, a place to port and profile software where the compile times are tolerable and the crypto is genuinely fast. On those terms it is the first RISC-V board I would actually choose rather than tolerate.&lt;/p&gt;
&lt;p&gt;Do not buy it if you want the virtualization host its spec sheet promises, at least not yet, and not on Bianbu. Do not buy it as a compile box either: a \$200 Raspberry Pi 5 does the same tree 2.11x faster, and nothing here changes that. The case for the price is the 32 GB of RAM and the 60 TOPS AI engine, and this review tests neither.&lt;/p&gt;
&lt;p&gt;The honest summary is that SpacemiT built genuinely competitive silicon and shipped it with a kernel that cannot load KVM. The hardware is ready. The software is not, and the documentation is worse than the software.&lt;/p&gt;
&lt;h3&gt;What is broken, restated plainly&lt;/h3&gt;
&lt;p&gt;Because these are the things that will cost you time if you buy one:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;KVM does not work on the SpacemiT-provided kernel. &lt;code&gt;modprobe kvm&lt;/code&gt; oopses in &lt;code&gt;jump_label_module_notify&lt;/code&gt; on Bianbu 4.0.4 with kernel 6.18.3-generic &lt;code&gt;#1.0.5.4&lt;/code&gt;. The hardware supports RVH 1.0; the shipped kernel cannot use it. If virtualization is why you are buying this board, you cannot use the image it arrives with.&lt;/li&gt;
&lt;li&gt;No process can use more than 8 of the 16 cores. The A100 cluster is reachable by writing a PID to &lt;code&gt;/proc/set_ai_thread&lt;/code&gt;, and those cores run ordinary code at 57.5% of X100 per-clock throughput. But the mask is &lt;code&gt;0-7&lt;/code&gt; or &lt;code&gt;8-15&lt;/code&gt;, never &lt;code&gt;0-15&lt;/code&gt;, so the whole chip is never available to one job. Every other benchmark here was produced by the eight X100 cores.&lt;/li&gt;
&lt;li&gt;A newer BSP kernel did not help. I installed &lt;code&gt;6.18.3-1.0.8.4&lt;/code&gt; from the porting repo and tried to boot it with kexec. The board did not come back and needed a power cycle. Notably, kexec also failed with a kernel that unquestionably has full K3 support, which suggests kexec itself is unreliable on this vendor firmware rather than the kernel being at fault.&lt;/li&gt;
&lt;li&gt;Ubuntu's generic 7.0.0 kernel is not a workaround. It carries one &lt;code&gt;spacemit&lt;/code&gt; config entry against the BSP's sixty-six, ships no K3 device trees, and lacks the &lt;code&gt;dwmac-spacemit-ethqos&lt;/code&gt; driver this board's network port needs. Booting it would produce an unreachable machine.&lt;/li&gt;
&lt;li&gt;The customs duty is real. \$241.09 on a \$934 order, payable on delivery.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;A follow-up is coming&lt;/h3&gt;
&lt;p&gt;Canonical officially supports the SpacemiT K3 starting with Ubuntu 26.10 "Stonking Stingray", and that is the interesting thread to pull. The supported path replaces SpacemiT's U-Boot with EDK2 UEFI firmware, flashed over fastboot with the board in FDL mode, and then installs Ubuntu from a USB stick.&lt;/p&gt;
&lt;p&gt;The reason I think it will matter: Ubuntu 26.10 ships kernel 7.3.0, and the mainline SpacemiT device tree pull request that adds K3 support was titled &lt;em&gt;RISC-V SpacemiT Devicetrees for v7.3&lt;/em&gt;. That is the first kernel where this board's support is upstream rather than carried in a vendor tree. It is the obvious candidate to fix the KVM oops, which is a kernel-side failure rather than a silicon one.&lt;/p&gt;
&lt;p&gt;I have the 26.10 daily image written to a USB stick and the flashing procedure mapped out. The next piece will cover whether official Ubuntu turns this from a fast compile box into the virtualization host it was supposed to be, and whether a mainline kernel lets one process schedule across all sixteen cores instead of eight.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;By the numbers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rust clean release build, pinned tree (&lt;code&gt;2821bba&lt;/code&gt;, 16,639 LOC): 161.80 s mean, 7th of 13 on my bench&lt;/li&gt;
&lt;li&gt;Rust clean release build, current tree (&lt;code&gt;b0cfcd4&lt;/code&gt;, 155,867 LOC): 1271.84 s mean, 0.09% CV&lt;/li&gt;
&lt;li&gt;vs SpacemiT's own previous generation: 4.02x faster than the Ky X1 in the Orange Pi RV2&lt;/li&gt;
&lt;li&gt;Single X100 core: 911 sysbench events/s, 94% of a Cortex-A76 at 2.35 GHz&lt;/li&gt;
&lt;li&gt;Geekbench 7: 363 single-core, 2019 multi-core (&lt;a href="https://browser.geekbench.com/v7/cpu/466791"&gt;result 466791&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;AES-128-GCM 2372.7 MB/s, ahead of a Cortex-A76 and 109x the Pine64 Star64&lt;/li&gt;
&lt;li&gt;Vector crypto contribution: 34.5x on AES-128-GCM, 10.4x on SHA-256&lt;/li&gt;
&lt;li&gt;Cores usable by one process: 8 of 16, either cluster but never both&lt;/li&gt;
&lt;li&gt;A100 cluster on the same compile: 342.78 s against X100's 161.29 s, 57.5% of X100 per clock&lt;/li&gt;
&lt;li&gt;KVM: broken, kernel oops on module load&lt;/li&gt;
&lt;li&gt;Thermals: 51 C idle, 65 C peak under sixteen-worker load, no throttling&lt;/li&gt;
&lt;li&gt;NVMe: 1.0 GB/s write, 2.1 GB/s read&lt;/li&gt;
&lt;li&gt;Cost: \$1,175.09 delivered, including \$241.09 customs duty&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Caveats. Every figure except the A100 compile and sysbench runs comes from the eight X100 cores. The A100 numbers are n=2 on the compile and single runs on sysbench. I did not test the A100 cluster on AI workloads at all, which is what it exists for, so its 60 TOPS rating remains SpacemiT's number rather than a measurement of mine. The compile workload uses &lt;code&gt;lto = true&lt;/code&gt; and &lt;code&gt;codegen-units = 1&lt;/code&gt;, which serialises the final link and understates wide machines. Commit &lt;code&gt;2821bba&lt;/code&gt; predates the project having a &lt;code&gt;Cargo.lock&lt;/code&gt;, so dependency versions resolve fresh. Network throughput was not measured. The sysbench comparison against the Cortex-A76 uses version 1.0.20 on both boards; the Star64 shipped 0.4.12 and its sysbench figures are not comparable to either.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Review Date: September 25, 2026&lt;/p&gt;
&lt;p&gt;Hardware Tested: SpacemiT K3 Pico-ITX, 32 GB RAM, 128 GB UFS, WD_BLACK SN850X 2 TB NVMe added&lt;/p&gt;
&lt;p&gt;OS Tested: Bianbu 4.0.4, kernel 6.18.3-generic &lt;code&gt;#1.0.5.4&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Benchmark Workload: &lt;a href="https://github.com/ajokela/ballistics-engine"&gt;ballistics-engine&lt;/a&gt; clean release builds at commits &lt;code&gt;2821bba&lt;/code&gt; and &lt;code&gt;b0cfcd4&lt;/code&gt;, rustc 1.98.1&lt;/p&gt;
&lt;p&gt;Conclusion: The first RISC-V board that competes with ARM on performance rather than on novelty, sold at a price that is defensible only if you need what it uniquely offers, and shipped with a kernel that disables half its cores and cannot start a virtual machine.&lt;/p&gt;</description><category>benchmarks</category><category>bianbu</category><category>hardware review</category><category>hypervisor extension</category><category>k3</category><category>kvm</category><category>openssl</category><category>pico-itx</category><category>risc v</category><category>riscv64</category><category>rust compilation</category><category>rva23</category><category>rvv</category><category>single board computers</category><category>spacemit</category><category>spacemit a100</category><category>spacemit x100</category><category>ubuntu</category><category>vector crypto</category><category>virtualization</category><guid>https://tinycomputers.io/posts/spacemit-k3-pico-itx-review.html</guid><pubDate>Fri, 25 Sep 2026 13:00:00 GMT</pubDate></item><item><title>Upgrading ROCm 7.0 to 7.2 on AMD Strix Halo (gfx1151)</title><link>https://tinycomputers.io/posts/upgrading-rocm-7.0-to-7.2-on-amd-strix-halo-gfx1151.html?utm_source=feed&amp;utm_medium=rss&amp;utm_campaign=rss</link><dc:creator>A.C. Jokela</dc:creator><description>&lt;div class="audio-widget"&gt;
&lt;div class="audio-widget-header"&gt;
&lt;span class="audio-widget-icon"&gt;🎧&lt;/span&gt;
&lt;span class="audio-widget-label"&gt;Listen to this article&lt;/span&gt;
&lt;/div&gt;
&lt;audio controls preload="metadata"&gt;
&lt;source src="https://tinycomputers.io/upgrading-rocm-7.0-to-7.2-on-amd-strix-halo-gfx1151_tts.mp3" type="audio/mpeg"&gt;
&lt;/source&gt;&lt;/audio&gt;
&lt;div class="audio-widget-footer"&gt;15 min · AI-generated narration&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;If you're running AMD's Strix Halo hardware -- specifically the Ryzen AI MAX+ 395 with its integrated Radeon 8060S GPU -- you already know the software ecosystem is a moving target. The gfx1151 architecture sits in an awkward spot: powerful hardware that isn't officially listed on AMD's ROCm support matrix, yet functional enough to run real workloads with the right driver stack. When ROCm 7.2 landed in early 2026, upgrading from 7.0.2 was a priority. The newer stack brings an updated HSA runtime, a refreshed amdgpu kernel module, and broader compatibility improvements that matter on bleeding-edge silicon.&lt;/p&gt;
&lt;p&gt;This post documents the complete upgrade procedure from ROCm 7.0.2 to 7.2 on a production Ubuntu 24.04 system. It's not a theoretical exercise -- this was performed on a live server running QEMU virtual machines and network services, with the expectation that everything would come back online after a single reboot.&lt;/p&gt;
&lt;p&gt;AMD's official documentation states that in-place ROCm upgrades are not supported. The recommended path is a full uninstall followed by a clean reinstall. That's exactly what we did, and the entire process took about 20 minutes of wall-clock time (excluding the reboot).&lt;/p&gt;
&lt;h3&gt;System Overview&lt;/h3&gt;
&lt;p&gt;The target system is a &lt;a href="https://go.tinymachines.io/s9YkzS"&gt;Bosgame mini PC&lt;/a&gt; running the Ryzen AI MAX+ 395 APU. If you've read the &lt;a href="https://tinycomputers.io/posts/amd-ai-max+-395-system-review-a-comprehensive-analysis/"&gt;earlier review&lt;/a&gt; of this hardware, you'll be familiar with the specs. For context on this upgrade, here's what matters:&lt;/p&gt;
&lt;h4&gt;Hardware&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU&lt;/strong&gt;: AMD Ryzen AI MAX+ 395, 16 cores / 32 threads, Zen 5&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU&lt;/strong&gt;: Integrated Radeon 8060S, 40 Compute Units, RDNA 3.5 (gfx1151)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory&lt;/strong&gt;: 32 GB DDR5, unified architecture with 96 GB allocatable to GPU&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Peak GPU Clock&lt;/strong&gt;: 2,900 MHz&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Software (Pre-Upgrade)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OS&lt;/strong&gt;: Ubuntu 24.04.3 LTS (Noble Numbat)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kernel&lt;/strong&gt;: 6.14.0-37-generic (HWE, pinned)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROCm&lt;/strong&gt;: 7.0.2&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;amdgpu-dkms&lt;/strong&gt;: 6.14.14 (from &lt;code&gt;repo.radeon.com/amdgpu/30.10.2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROCk Module&lt;/strong&gt;: 6.14.14&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Running Services&lt;/h4&gt;
&lt;p&gt;The system was actively serving several roles during the upgrade:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Five QEMU virtual machines (three x86, two aarch64)&lt;/li&gt;
&lt;li&gt;A PXE boot server (dnsmasq) for the local network&lt;/li&gt;
&lt;li&gt;Docker daemon with various containers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these services are tied to the GPU driver stack, so the plan was to perform the upgrade and reboot without shutting them down first. The VMs and network services would come back automatically after the reboot.&lt;/p&gt;
&lt;h3&gt;Why Upgrade&lt;/h3&gt;
&lt;p&gt;ROCm 7.0.2 worked on this hardware. Models loaded, inference ran, &lt;code&gt;rocminfo&lt;/code&gt; detected the GPU. So why bother upgrading?&lt;/p&gt;
&lt;p&gt;Three reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Driver maturity for gfx1151&lt;/strong&gt;: The amdgpu kernel module jumped from 6.14.14 to 6.16.13 between the two releases. That's not a minor revision -- it represents months of kernel driver development. On hardware that isn't officially supported, newer drivers tend to bring meaningful stability improvements as AMD's internal teams encounter and fix issues on adjacent architectures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HSA Runtime improvements&lt;/strong&gt;: ROCm 7.2 ships HSA Runtime Extension version 1.15, up from 1.11 in ROCm 7.0.2. The HSA (Heterogeneous System Architecture) runtime is the lowest layer of the ROCm software stack -- it handles device discovery, memory management, and kernel dispatch. Improvements here affect everything built on top of it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ecosystem alignment&lt;/strong&gt;: PyTorch wheels, Ollama builds, and other ROCm-dependent tools increasingly target 7.2 as the baseline. Running 7.0.2 was becoming an exercise in version pinning and compatibility workarounds.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;The Kernel Hold: Why It Matters&lt;/h3&gt;
&lt;p&gt;Before diving into the procedure, a note on kernel management. This system runs the Ubuntu HWE (Hardware Enablement) kernel, which provides newer kernel versions on LTS releases. At the time of this upgrade, the HWE kernel was 6.14.0-37-generic. The upstream kernel had already moved to 6.17, but we didn't want the ROCm upgrade to pull in a kernel that AMD's DKMS module might not build against.&lt;/p&gt;
&lt;p&gt;The solution is &lt;code&gt;apt-mark hold&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt-mark&lt;span class="w"&gt; &lt;/span&gt;hold&lt;span class="w"&gt; &lt;/span&gt;linux-generic-hwe-24.04&lt;span class="w"&gt; &lt;/span&gt;linux-headers-generic-hwe-24.04&lt;span class="w"&gt; &lt;/span&gt;linux-image-generic-hwe-24.04
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This prevents &lt;code&gt;apt&lt;/code&gt; from upgrading the kernel meta-packages, effectively pinning the system to 6.14.0-37-generic. The hold was already in place before the upgrade and remained untouched throughout. After the upgrade, we confirmed it was still active:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;apt-mark&lt;span class="w"&gt; &lt;/span&gt;showhold
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;linux-generic-hwe-24.04
linux-headers-generic-hwe-24.04
linux-image-generic-hwe-24.04
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you're running Strix Halo or any other hardware where kernel compatibility with &lt;code&gt;amdgpu-dkms&lt;/code&gt; is uncertain, kernel holds are essential. A kernel upgrade that breaks the DKMS build means no GPU driver after reboot.&lt;/p&gt;
&lt;h3&gt;Upgrade Procedure&lt;/h3&gt;
&lt;h4&gt;Step 1: Uninstall the Current ROCm Stack&lt;/h4&gt;
&lt;p&gt;AMD provides the &lt;code&gt;amdgpu-uninstall&lt;/code&gt; script for exactly this purpose. It removes all ROCm userspace packages and the amdgpu-dkms kernel module in a single operation:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;amdgpu-uninstall&lt;span class="w"&gt; &lt;/span&gt;-y
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command removed approximately 120 packages, including the full HIP runtime, rocBLAS, MIOpen, MIGraphX, ROCm SMI, the LLVM-based compiler toolchain, and the Mesa graphics drivers that ship with ROCm. The DKMS module was purged, which means the amdgpu kernel module was removed from the 6.14.0-37-generic kernel's module tree.&lt;/p&gt;
&lt;p&gt;After the ROCm stack was removed, we purged the &lt;code&gt;amdgpu-install&lt;/code&gt; meta-package itself:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;purge&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;amdgpu-install
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This also cleaned up the APT repository entries that &lt;code&gt;amdgpu-install&lt;/code&gt; had configured in &lt;code&gt;/etc/apt/sources.list.d/&lt;/code&gt;. The old repos -- &lt;code&gt;repo.radeon.com/amdgpu/30.10.2&lt;/code&gt;, &lt;code&gt;repo.radeon.com/rocm/apt/7.0.2&lt;/code&gt;, and &lt;code&gt;repo.radeon.com/graphics/7.0.2&lt;/code&gt; -- were all removed automatically.&lt;/p&gt;
&lt;h4&gt;Step 2: Clean Up Leftover Files&lt;/h4&gt;
&lt;p&gt;The package removal was thorough but not perfect. A few leftover directories remained in &lt;code&gt;/opt/&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/opt/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;rocm
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;rocm-7.0.0
rocm-7.0.2
rocm-7.9.0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;rocm-7.0.0&lt;/code&gt; directory was from a previous installation attempt. The &lt;code&gt;rocm-7.9.0&lt;/code&gt; was from an earlier experiment with a release candidate build. The &lt;code&gt;rocm-7.0.2&lt;/code&gt; directory contained a single orphaned shared library (&lt;code&gt;libamdhip64.so.6&lt;/code&gt;) that dpkg couldn't remove because the directory wasn't empty. All three were cleaned up manually:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;/opt/rocm-7.0.0&lt;span class="w"&gt; &lt;/span&gt;/opt/rocm-7.0.2&lt;span class="w"&gt; &lt;/span&gt;/opt/rocm-7.9.0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's worth checking for stale ROCm directories after any uninstall. They consume negligible disk space but can confuse build systems and scripts that scan &lt;code&gt;/opt/rocm*&lt;/code&gt; for active installations.&lt;/p&gt;
&lt;h4&gt;Step 3: Install the ROCm 7.2 Installer&lt;/h4&gt;
&lt;p&gt;AMD distributes ROCm through a meta-package called &lt;code&gt;amdgpu-install&lt;/code&gt;. Each ROCm release has its own version of this package, which configures the appropriate APT repositories. The 7.2 installer was downloaded directly from AMD's repository:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp
wget&lt;span class="w"&gt; &lt;/span&gt;https://repo.radeon.com/amdgpu-install/7.2/ubuntu/noble/amdgpu-install_7.2.70200-1_all.deb
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;./amdgpu-install_7.2.70200-1_all.deb
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;update
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After installation and &lt;code&gt;apt update&lt;/code&gt;, three new repositories were active:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://repo.radeon.com/amdgpu/30.30/ubuntu noble&lt;/code&gt; -- the kernel driver and Mesa components&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://repo.radeon.com/rocm/apt/7.2 noble&lt;/code&gt; -- the ROCm userspace stack&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://repo.radeon.com/graphics/7.2/ubuntu noble&lt;/code&gt; -- graphics libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The version numbering can be confusing. The &lt;code&gt;amdgpu-install&lt;/code&gt; package version is &lt;code&gt;30.30.0.0.30300000-2278356.24.04&lt;/code&gt;, which maps to the amdgpu driver release 30.30. The ROCm version is 7.2.0. These are different version tracks that AMD maintains in parallel.&lt;/p&gt;
&lt;h4&gt;Step 4: Install ROCm 7.2&lt;/h4&gt;
&lt;p&gt;With the repositories configured, the actual installation was a single command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;amdgpu-install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;--usecase&lt;span class="o"&gt;=&lt;/span&gt;graphics,rocm
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;--usecase=graphics,rocm&lt;/code&gt; flag tells the installer to include both the Mesa graphics drivers and the full ROCm compute stack. This is the right choice for a system that needs both display output and GPU compute capabilities.&lt;/p&gt;
&lt;p&gt;The installation took approximately 10 minutes and included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;amdgpu-dkms 6.16.13&lt;/strong&gt;: The kernel module, compiled via DKMS against the running kernel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full ROCm 7.2 stack&lt;/strong&gt;: HIP runtime, hipcc compiler, rocBLAS, rocFFT, MIOpen, MIGraphX, RCCL, ROCm SMI, ROCProfiler, and dozens of other libraries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mesa graphics&lt;/strong&gt;: Updated EGL, OpenGL, and Vulkan drivers from the amdgpu Mesa fork&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROCm LLVM toolchain&lt;/strong&gt;: The LLVM-based compiler infrastructure that HIP uses for kernel compilation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The DKMS build is the critical step. During installation, DKMS compiled the amdgpu module against the kernel headers for 6.14.0-37-generic. The output confirmed a successful build:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;depmod...
update-initramfs: Generating /boot/initrd.img-6.14.0-37-generic
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The initramfs was regenerated to include the new module, ensuring it would be loaded at boot.&lt;/p&gt;
&lt;h4&gt;Step 5: Verify DKMS&lt;/h4&gt;
&lt;p&gt;Before rebooting, we confirmed the DKMS status:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;dkms&lt;span class="w"&gt; &lt;/span&gt;status
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;amdgpu/6.16.13-2278356.24.04, 6.14.0-37-generic, x86_64: installed
virtualbox/7.0.16, 6.14.0-36-generic, x86_64: installed
virtualbox/7.0.16, 6.14.0-37-generic, x86_64: installed
virtualbox/7.0.16, 6.8.0-100-generic, x86_64: installed
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The new amdgpu module (6.16.13) was built and installed for 6.14.0-37-generic. Note that it only built for the currently running kernel, unlike VirtualBox which had modules built for older kernels as well. This is expected -- DKMS builds against available kernel headers, and the old kernel headers for 6.14.0-36 and 6.8.0-100 were still present from earlier installations.&lt;/p&gt;
&lt;h4&gt;Step 6: Reboot&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The server came back online in approximately 50 seconds.&lt;/p&gt;
&lt;h3&gt;Post-Reboot Verification&lt;/h3&gt;
&lt;h4&gt;rocminfo&lt;/h4&gt;
&lt;p&gt;The first check after reboot was &lt;code&gt;rocminfo&lt;/code&gt;, which queries the HSA runtime for available agents:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;rocminfo
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;ROCk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.16&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loaded&lt;/span&gt;
&lt;span class="o"&gt;=====================&lt;/span&gt;
&lt;span class="n"&gt;HSA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Attributes&lt;/span&gt;
&lt;span class="o"&gt;=====================&lt;/span&gt;
&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mf"&gt;1.18&lt;/span&gt;
&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mf"&gt;1.15&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==========&lt;/span&gt;
&lt;span class="n"&gt;HSA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Agents&lt;/span&gt;
&lt;span class="o"&gt;==========&lt;/span&gt;
&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AMD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RYZEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MAX&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;395&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Radeon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8060&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CPU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gfx1151&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GPU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Marketing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;AMD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Radeon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graphics&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Compute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Clock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Freq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MHz&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;2900&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;APU&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ISA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amdgcn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;amd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;amdhsa&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;gfx1151&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ISA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amdgcn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;amd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;amdhsa&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;gfx11&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;generic&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Key observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ROCk module 6.16.13&lt;/strong&gt;: The new kernel module loaded successfully.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime Ext Version 1.15&lt;/strong&gt;: Upgraded from 1.11 in ROCm 7.0.2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gfx1151 detected&lt;/strong&gt;: The GPU was recognized with its correct ISA identifier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gfx11-generic ISA&lt;/strong&gt;: ROCm 7.2 also exposes a generic gfx11 ISA, which allows software compiled for the broader RDNA 3 family to run on this device without gfx1151-specific builds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;APU memory&lt;/strong&gt;: The memory properties correctly identify this as an APU with unified memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;ROCm SMI&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;rocm-smi
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;Device  Node  Temp    Power     SCLK  MCLK     Fan  Perf  VRAM%  GPU%
0       1     33.0C   9.087W    N/A   1000Mhz  0%   auto  0%     0%
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The GPU was visible and reporting telemetry. The 0% VRAM reading is expected on an APU -- &lt;code&gt;rocm-smi&lt;/code&gt; reports dedicated VRAM usage, but on a unified memory architecture, GPU memory allocations come from system RAM and aren't reflected in this counter.&lt;/p&gt;
&lt;h4&gt;ROCm Version&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/opt/rocm/.info/version
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="mf"&gt;7.2.0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;DKMS&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;dkms&lt;span class="w"&gt; &lt;/span&gt;status
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Confirmed &lt;code&gt;amdgpu/6.16.13&lt;/code&gt; remained installed for 6.14.0-37-generic after reboot.&lt;/p&gt;
&lt;h3&gt;PyTorch Validation&lt;/h3&gt;
&lt;p&gt;With the driver stack verified, the next step was confirming that PyTorch could see and use the GPU. ROCm 7.2 ships with prebuilt PyTorch wheels on AMD's repository.&lt;/p&gt;
&lt;h4&gt;Installing PyTorch for ROCm 7.2&lt;/h4&gt;
&lt;p&gt;We set up a Python virtual environment and installed the ROCm-specific wheels:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;venv&lt;span class="w"&gt; &lt;/span&gt;.venv
&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.venv/bin/activate
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--upgrade&lt;span class="w"&gt; &lt;/span&gt;pip
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The PyTorch wheel for ROCm 7.2 requires a matching ROCm-specific build of Triton. Both are available from AMD's manylinux repository. The order matters -- Triton must be installed first, since the PyTorch wheel declares it as a dependency with a specific version that doesn't exist on PyPI:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2/triton-3.5.1%2Brocm7.2.0.gita272dfa8-cp312-cp312-linux_x86_64.whl
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2/torch-2.9.1%2Brocm7.2.0.lw.git7e1940d4-cp312-cp312-linux_x86_64.whl
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2/torchvision-0.24.0%2Brocm7.2.0.gitb919bd0c-cp312-cp312-linux_x86_64.whl
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are the ROCm 7.2 builds for Python 3.12. AMD also provides wheels for Python 3.10, 3.11, and 3.13.&lt;/p&gt;
&lt;h4&gt;Smoke Test&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;torch&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PyTorch:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CUDA available:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Device:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_device_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"VRAM:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_device_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_memory&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1e9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"GB"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;PyTorch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;rocm7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;git7e1940d4&lt;/span&gt;
&lt;span class="n"&gt;CUDA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;Device&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AMD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Radeon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graphics&lt;/span&gt;
&lt;span class="n"&gt;VRAM&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;103.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GB&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;PyTorch detected the GPU through ROCm's HIP-to-CUDA translation layer. The 103.1 GB figure represents the total addressable memory on this unified-memory APU, which includes both the 96 GB GPU allocation and additional system memory accessible through the HSA runtime.&lt;/p&gt;
&lt;p&gt;Note the use of &lt;code&gt;torch.cuda&lt;/code&gt; despite this being an AMD GPU. ROCm's HIP runtime presents itself through PyTorch's CUDA interface, so all CUDA API calls in PyTorch (device selection, memory management, kernel launches) work transparently with AMD hardware.&lt;/p&gt;
&lt;h3&gt;Before and After Summary&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;ROCm 7.0.2&lt;/th&gt;
&lt;th&gt;ROCm 7.2.0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ROCm Version&lt;/td&gt;
&lt;td&gt;7.0.2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.2.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amdgpu-dkms&lt;/td&gt;
&lt;td&gt;6.14.14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.16.13&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROCk Module&lt;/td&gt;
&lt;td&gt;6.14.14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.16.13&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HSA Runtime Ext&lt;/td&gt;
&lt;td&gt;1.11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.15&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amdgpu Repo&lt;/td&gt;
&lt;td&gt;30.10.2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30.30&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PyTorch&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;2.9.1+rocm7.2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Triton&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;3.5.1+rocm7.2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;6.14.0-37-generic&lt;/td&gt;
&lt;td&gt;6.14.0-37-generic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel Holds&lt;/td&gt;
&lt;td&gt;In place&lt;/td&gt;
&lt;td&gt;In place&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Notes on gfx1151 Support&lt;/h3&gt;
&lt;p&gt;It's worth being explicit about the support situation. As of February 2026, gfx1151 (Strix Halo) is &lt;strong&gt;not listed&lt;/strong&gt; on AMD's official ROCm support matrix. The supported RDNA 3 targets are gfx1100 (Navi 31, RX 7900 XTX) and gfx1101 (Navi 32). Strix Halo's gfx1151 is an RDNA 3.5 derivative that shares much of the ISA with gfx1100 but has architectural differences in the memory subsystem and compute unit layout.&lt;/p&gt;
&lt;p&gt;In practice, ROCm 7.2 works on gfx1151. The kernel driver loads, &lt;code&gt;rocminfo&lt;/code&gt; detects the GPU, and PyTorch can allocate tensors and dispatch compute kernels. The &lt;code&gt;gfx11-generic&lt;/code&gt; ISA target in ROCm 7.2 is particularly helpful -- it provides a compatibility path for software that hasn't been explicitly compiled for gfx1151.&lt;/p&gt;
&lt;p&gt;However, "works" and "fully supported" are different things. There are known quirks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;rocm-smi VRAM reporting&lt;/strong&gt;: Always shows 0% on the APU since it only tracks discrete VRAM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No official PyTorch gfx1151 builds&lt;/strong&gt;: The ROCm PyTorch wheels target gfx1100. They run on gfx1151 through ISA compatibility, but performance may not be optimal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Large model loading latency&lt;/strong&gt;: Moving large models to the GPU device can be slow on the unified memory architecture, as the HSA runtime handles page migration differently than discrete GPU DMA transfers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you're considering this hardware for production AI workloads, treat ROCm support as "functional but experimental." It works well enough for development, testing, and moderate inference workloads. For production training or latency-sensitive deployment, stick with hardware on AMD's official support list.&lt;/p&gt;
&lt;h3&gt;Rollback Plan&lt;/h3&gt;
&lt;p&gt;If the upgrade fails -- the DKMS module doesn't build, the GPU isn't detected after reboot, or something else goes wrong -- the rollback path is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Uninstall ROCm 7.2:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;amdgpu-uninstall&lt;span class="w"&gt; &lt;/span&gt;-y
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;purge&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;amdgpu-install
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Reinstall ROCm 7.0.2:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;wget&lt;span class="w"&gt; &lt;/span&gt;https://repo.radeon.com/amdgpu-install/30.10.2/ubuntu/noble/amdgpu-install_30.10.2.0.30100200-2226257.24.04_all.deb
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;./amdgpu-install_30.10.2.0.30100200-2226257.24.04_all.deb
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;update
sudo&lt;span class="w"&gt; &lt;/span&gt;amdgpu-install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;--usecase&lt;span class="o"&gt;=&lt;/span&gt;graphics,rocm
sudo&lt;span class="w"&gt; &lt;/span&gt;reboot
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The entire rollback takes about 15 minutes. Keep the old &lt;code&gt;amdgpu-install&lt;/code&gt; deb URL handy -- it's not linked from AMD's current download pages once a newer version is published.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Upgrading ROCm on hardware that isn't officially supported always carries some risk, but this upgrade from 7.0.2 to 7.2 on gfx1151 was uneventful. The procedure follows AMD's documented uninstall-reinstall approach with no deviations. The kernel hold strategy kept the kernel stable, the DKMS module built cleanly against 6.14.0-37-generic, and all post-reboot checks passed.&lt;/p&gt;
&lt;p&gt;The improvements in ROCm 7.2 -- particularly the HSA runtime bump to 1.15 and the introduction of the &lt;code&gt;gfx11-generic&lt;/code&gt; ISA target -- represent meaningful progress for Strix Halo users. The ecosystem is slowly catching up to the hardware. It's not there yet, but each release closes the gap.&lt;/p&gt;
&lt;p&gt;For anyone running a Ryzen AI MAX+ 395 or similar Strix Halo hardware on Ubuntu 24.04, this upgrade is worth doing. The procedure is well-defined, the rollback path is clear, and the newer driver stack brings tangible benefits. Just remember to hold your kernel first.&lt;/p&gt;
&lt;h3&gt;Recommended Resources&lt;/h3&gt;
&lt;h4&gt;Hardware&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.tinymachines.io/s9YkzS"&gt;Bosgame M5 AI Mini PC (Ryzen AI MAX+ 395)&lt;/a&gt; - The system used in this post&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.tinymachines.io/KM2G6z"&gt;GMKtec EVO X2 (Ryzen AI MAX+ 395)&lt;/a&gt; - Another Strix Halo mini PC option on Amazon&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Books&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.tinymachines.io/TPE3Gv"&gt;&lt;em&gt;Deep Learning with PyTorch&lt;/em&gt;&lt;/a&gt; by Stevens, Antiga, Huang, Viehmann - Comprehensive guide to building, training, and tuning neural networks with PyTorch&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.tinymachines.io/Ze2REq"&gt;&lt;em&gt;Programming PyTorch for Deep Learning&lt;/em&gt;&lt;/a&gt; by Ian Pointer - Practical guide to creating and deploying deep learning applications&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go.tinymachines.io/YxrRZe"&gt;&lt;em&gt;Understanding Deep Learning&lt;/em&gt;&lt;/a&gt; by Simon Prince - Modern treatment of deep learning fundamentals&lt;/li&gt;
&lt;/ul&gt;</description><category>amd</category><category>amdgpu</category><category>dkms</category><category>driver upgrade</category><category>gfx1151</category><category>gpu computing</category><category>linux</category><category>pytorch</category><category>rocm</category><category>ryzen ai</category><category>strix halo</category><category>ubuntu</category><guid>https://tinycomputers.io/posts/upgrading-rocm-7.0-to-7.2-on-amd-strix-halo-gfx1151.html</guid><pubDate>Wed, 18 Feb 2026 16:00:00 GMT</pubDate></item></channel></rss>