<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tutorials |</title><link>https://www.isarvin.com/categories/tutorials/</link><atom:link href="https://www.isarvin.com/categories/tutorials/index.xml" rel="self" type="application/rss+xml"/><description>Tutorials</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-gb</language><lastBuildDate>Mon, 08 Jun 2026 00:00:00 +0000</lastBuildDate><image><url>https://www.isarvin.com/media/sharing.png</url><title>Tutorials</title><link>https://www.isarvin.com/categories/tutorials/</link></image><item><title>Emulating an Old-World LoongArch64 VM with QEMU</title><link>https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/</link><pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate><guid>https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/</guid><description>&lt;p&gt;LoongArch64 has an awkward bit of history: the ecosystem is often described as split between
. For the kind of server images I care about here, especially KylinOS Server and UnionTech OS Server, the practical target is still old-world compatibility.&lt;/p&gt;
&lt;p&gt;That is why the firmware file matters. A newer LoongArch64 UEFI build is not automatically better for these guests. For this workflow, &lt;code&gt;QEMU_EFI_7.2.fd&lt;/code&gt; is the boringly useful choice: old enough to match the systems being installed, but still aligned with QEMU&amp;rsquo;s LoongArch64 support era.&lt;/p&gt;
&lt;p&gt;I have shared my copy here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The file I am using is 3,801,088 bytes. Its SHA256 checksum is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;59ab6e18af0ca75ff1556ebb4b6ad640ffbf09499710e655172254f93a2d63ba
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can verify it after downloading:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shasum -a &lt;span class="m"&gt;256&lt;/span&gt; QEMU_EFI_7.2.fd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="note"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8l.8-2.685a4.5 4.5 0 0 1 1.13-1.897zm0 0L19.5 7.125"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Note&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;This post is about full-system emulation, not native virtualisation. On an x86_64 or Apple Silicon host, LoongArch64 can be quite slow. If a guest has not reached the installer or booted system after a long time, the problem may be firmware, guest compatibility, or host QEMU support rather than just patience.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;details class="print:hidden xl:hidden" open&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;div class="text-sm"&gt;
&lt;nav id="TableOfContents"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#install-qemu"&gt;Install QEMU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#prepare-the-files"&gt;Prepare the files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#boot-from-an-iso"&gt;Boot from an ISO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#boot-an-installed-qcow2"&gt;Boot an installed qcow2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#use-qemu-monitor"&gt;Use QEMU Monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#confirm-the-architecture"&gt;Confirm the architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#optional-use-utm-on-macos"&gt;Optional: use UTM on macOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;div class="hb-steps"&gt;
&lt;h3 id="install-qemu"&gt;Install QEMU&lt;/h3&gt;
&lt;p&gt;On macOS, Homebrew is the easiest way to install QEMU:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-system-loongarch64 --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-system-loongarch64 -machine &lt;span class="nb"&gt;help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-system-loongarch64 -cpu &lt;span class="nb"&gt;help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On Linux, install the package that provides &lt;code&gt;qemu-system-loongarch64&lt;/code&gt;. For example, on a Fedora-like host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dnf install -y qemu-system-loongarch64 qemu-img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;QEMU 7.2 or newer is strongly preferred. QEMU 7.1 introduced initial LoongArch support, and QEMU 7.2 included important updates. In practice, I would not spend time debugging old QEMU builds for this.&lt;/p&gt;
&lt;p&gt;UTM is worth knowing about on macOS because it is a pleasant QEMU-based VM manager. It can emulate a LoongArch64 VM, so the point is not that UTM is unavailable. For this old-world cloud-image workflow, though, I still prefer the direct &lt;code&gt;qemu-system-loongarch64&lt;/code&gt; command because the firmware, device, monitor, and forwarding options are easier to reproduce exactly.&lt;/p&gt;
&lt;h3 id="prepare-the-files"&gt;Prepare the files&lt;/h3&gt;
&lt;p&gt;Create a working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p ~/VMs/loongarch64
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/VMs/loongarch64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Put these files in that directory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;QEMU_EFI_7.2.fd&lt;/code&gt;, downloaded from one of the cloud-drive links above.&lt;/li&gt;
&lt;li&gt;A LoongArch64 server ISO, such as KylinOS Server or UnionTech OS Server.&lt;/li&gt;
&lt;li&gt;A qcow2 disk created for the VM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create the disk with &lt;code&gt;qemu-img&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-img create -f qcow2 KylinOS-Server-loongarch64.qcow2 40G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Adjust the filename and size for your own guest. These systems are Red Hat-like, so a 40 GB disk is a reasonable starting point for a small build or image-preparation VM.&lt;/p&gt;
&lt;h3 id="boot-from-an-iso"&gt;Boot from an ISO&lt;/h3&gt;
&lt;p&gt;Use this shape when installing a fresh LoongArch64 server guest from an ISO:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-system-loongarch64 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -M virt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -bios QEMU_EFI_7.2.fd &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -cdrom KylinOS-Server-loongarch64.iso &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -chardev socket,id&lt;span class="o"&gt;=&lt;/span&gt;qemu-ga.0,path&lt;span class="o"&gt;=&lt;/span&gt;qemu-ga.sock,server&lt;span class="o"&gt;=&lt;/span&gt;on,wait&lt;span class="o"&gt;=&lt;/span&gt;off &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -cpu la464-loongarch-cpu &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device nec-usb-xhci,id&lt;span class="o"&gt;=&lt;/span&gt;xhci,addr&lt;span class="o"&gt;=&lt;/span&gt;0x1b &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device usb-kbd,id&lt;span class="o"&gt;=&lt;/span&gt;keyboard,bus&lt;span class="o"&gt;=&lt;/span&gt;xhci.0,port&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device usb-tablet,id&lt;span class="o"&gt;=&lt;/span&gt;tablet,bus&lt;span class="o"&gt;=&lt;/span&gt;xhci.0,port&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device virtio-gpu-pci &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device virtio-serial &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device virtserialport,chardev&lt;span class="o"&gt;=&lt;/span&gt;qemu-ga.0,name&lt;span class="o"&gt;=&lt;/span&gt;org.qemu.guest_agent.0 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -hda KylinOS-Server-loongarch64.qcow2 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -m 2G &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -monitor telnet:localhost:5555,server&lt;span class="o"&gt;=&lt;/span&gt;on,wait&lt;span class="o"&gt;=&lt;/span&gt;off &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -net nic,model&lt;span class="o"&gt;=&lt;/span&gt;virtio &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -net user,hostfwd&lt;span class="o"&gt;=&lt;/span&gt;tcp::10000-:22 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -smp &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -vnc 0.0.0.0:1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For UnionTech OS Server, keep the same command structure and replace the ISO and qcow2 filenames. The main point is not the exact distribution name; it is the combination of &lt;code&gt;qemu-system-loongarch64&lt;/code&gt;, &lt;code&gt;-M virt&lt;/code&gt;, &lt;code&gt;QEMU_EFI_7.2.fd&lt;/code&gt;, &lt;code&gt;la464-loongarch-cpu&lt;/code&gt;, VirtIO devices, VNC, QEMU Guest Agent wiring, and SSH port forwarding.&lt;/p&gt;
&lt;p&gt;Connect with a VNC viewer to the host&amp;rsquo;s display &lt;code&gt;:1&lt;/code&gt;, which normally means TCP port &lt;code&gt;5901&lt;/code&gt;. If you add &lt;code&gt;--serial stdio&lt;/code&gt;, QEMU will also print serial output in the terminal, which can be useful when the graphical display is quiet.&lt;/p&gt;
&lt;p&gt;During installation, treat the guest as a Red Hat-like system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use VirtIO-backed disk and network devices.&lt;/li&gt;
&lt;li&gt;Install or keep &lt;code&gt;qemu-guest-agent&lt;/code&gt; if the image-preparation workflow needs guest-agent operations later.&lt;/li&gt;
&lt;li&gt;Ensure SSH is installed and enabled if you want to log in through the forwarded host port.&lt;/li&gt;
&lt;li&gt;Keep the firmware file and qcow2 disk together so the boot command stays reproducible.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="boot-an-installed-qcow2"&gt;Boot an installed qcow2&lt;/h3&gt;
&lt;p&gt;After installation, stop the emulation session and boot from the qcow2 disk without &lt;code&gt;-cdrom&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-system-loongarch64 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -M virt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -bios QEMU_EFI_7.2.fd &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -chardev socket,id&lt;span class="o"&gt;=&lt;/span&gt;qemu-ga.0,path&lt;span class="o"&gt;=&lt;/span&gt;qemu-ga.sock,server&lt;span class="o"&gt;=&lt;/span&gt;on,wait&lt;span class="o"&gt;=&lt;/span&gt;off &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -cpu la464-loongarch-cpu &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device nec-usb-xhci,id&lt;span class="o"&gt;=&lt;/span&gt;xhci,addr&lt;span class="o"&gt;=&lt;/span&gt;0x1b &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device usb-kbd,id&lt;span class="o"&gt;=&lt;/span&gt;keyboard,bus&lt;span class="o"&gt;=&lt;/span&gt;xhci.0,port&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device usb-tablet,id&lt;span class="o"&gt;=&lt;/span&gt;tablet,bus&lt;span class="o"&gt;=&lt;/span&gt;xhci.0,port&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device virtio-gpu-pci &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device virtio-serial &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -device virtserialport,chardev&lt;span class="o"&gt;=&lt;/span&gt;qemu-ga.0,name&lt;span class="o"&gt;=&lt;/span&gt;org.qemu.guest_agent.0 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -hda KylinOS-Server-loongarch64.qcow2 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -m 2G &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -monitor telnet:localhost:5555,server&lt;span class="o"&gt;=&lt;/span&gt;on,wait&lt;span class="o"&gt;=&lt;/span&gt;off &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -net nic,model&lt;span class="o"&gt;=&lt;/span&gt;virtio &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -net user,hostfwd&lt;span class="o"&gt;=&lt;/span&gt;tcp::10000-:22 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -smp &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -vnc 0.0.0.0:1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then SSH through the forwarded port:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh root@localhost -p &lt;span class="m"&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the guest has no network, first use VNC or serial output to inspect it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ip addr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl status sshd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl status qemu-guest-agent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On KylinOS Server or UnionTech OS Server, the package-management commands should feel familiar if you have used RHEL-like systems:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dnf install -y openssh-server qemu-guest-agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now sshd qemu-guest-agent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="use-qemu-monitor"&gt;Use QEMU Monitor&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;-monitor telnet:localhost:5555,server=on,wait=off&lt;/code&gt; option opens a QEMU Monitor endpoint. Connect to it from the host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;telnet localhost &lt;span class="m"&gt;5555&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Common monitor commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;info status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;info block
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;system_powerdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;system_reset
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;quit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;system_powerdown&lt;/code&gt; is the polite option if the guest supports ACPI shutdown. &lt;code&gt;quit&lt;/code&gt; stops QEMU directly and can lose guest data, so I only use it when the VM is disposable or already stuck.&lt;/p&gt;
&lt;p&gt;For more monitor commands, see the
.&lt;/p&gt;
&lt;h3 id="confirm-the-architecture"&gt;Confirm the architecture&lt;/h3&gt;
&lt;p&gt;Inside the guest:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uname -m
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uname -a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first command should print:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;loongarch64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At that point the VM is ready for the usual Red Hat-like server chores: package updates, qemu-guest-agent checks, image clean-up, kernel clean-up, and whatever build or compatibility work needed a LoongArch64 environment in the first place.&lt;/p&gt;
&lt;h3 id="optional-use-utm-on-macos"&gt;Optional: use UTM on macOS&lt;/h3&gt;
&lt;p&gt;If you prefer a GUI, UTM can be configured for this kind of VM as well. I would treat it as a convenience path rather than the main recipe, because the command line is easier to copy, audit, and rerun during image work.&lt;/p&gt;
&lt;p&gt;The important system settings are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Architecture: &lt;code&gt;LoongArch64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;System: &lt;code&gt;QEMU LoongArch Virtual Machine (default) (virt)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;CPU: &lt;code&gt;Default&lt;/code&gt;, or the closest available LoongArch CPU setting exposed by your UTM build.&lt;/li&gt;
&lt;li&gt;Memory and CPU cores: choose values that match your host. LoongArch64 emulation is slow, and checking &lt;code&gt;Force Multicore&lt;/code&gt; may help sometimes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="UTM system settings for a LoongArch64 QEMU virt machine."
srcset="https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-loongarch64-system_hu_17b1900a04fad702.webp 320w, https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-loongarch64-system_hu_a5924c7faa16bc13.webp 480w, https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-loongarch64-system_hu_cf8130110d3023bd.webp 700w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-loongarch64-system_hu_17b1900a04fad702.webp"
width="700"
height="369"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;QEMU_EFI_7.2.fd&lt;/code&gt; as a drive with image type &lt;code&gt;BIOS&lt;/code&gt;, and keep it read-only.&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="UTM drive settings showing QEMU_EFI_7.2.fd as a BIOS image."
srcset="https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-bios-drive_hu_4dbabf08fe722a7e.webp 320w, https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-bios-drive_hu_adcceb036749e08e.webp 480w, https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-bios-drive_hu_ea81d84dff8896d8.webp 700w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-bios-drive_hu_4dbabf08fe722a7e.webp"
width="700"
height="368"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;For display, &lt;code&gt;virtio-ramfb&lt;/code&gt; is a useful starting point.&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="UTM display settings using virtio-ramfb."
srcset="https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-virtio-ramfb-display_hu_a5d22c2b3d75e1da.webp 320w, https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-virtio-ramfb-display_hu_2a54e78b93a1066e.webp 480w, https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-virtio-ramfb-display_hu_f4421257622eef82.webp 700w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://www.isarvin.com/blog/emulate-loongarch64-vm-with-qemu/utm-virtio-ramfb-display_hu_a5d22c2b3d75e1da.webp"
width="700"
height="368"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;You still need the same guest-side checks after installation: SSH, &lt;code&gt;qemu-guest-agent&lt;/code&gt; if required, VirtIO networking, and &lt;code&gt;uname -m&lt;/code&gt; showing &lt;code&gt;loongarch64&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>Cleaning Up Unused Linux Kernels</title><link>https://www.isarvin.com/blog/clean-unused-linux-kernels/</link><pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate><guid>https://www.isarvin.com/blog/clean-unused-linux-kernels/</guid><description>&lt;p&gt;After installing another Linux kernel version, the old one usually stays installed. This is not a bug. It is a useful rollback path when the new kernel fails to boot, lacks a driver, or behaves strangely.&lt;/p&gt;
&lt;p&gt;Once the system has been rebooted and confirmed healthy, though, keeping too many old kernels becomes a quiet kind of clutter. &lt;code&gt;/boot&lt;/code&gt; gets noisy, GRUB entries pile up, and small boot partitions can eventually complain.&lt;/p&gt;
&lt;p&gt;This is the checklist I would want before touching a real machine: confirm what is running, remove only the obsolete package set, refresh the boot menu, and leave a fallback kernel unless the host is truly disposable.&lt;/p&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-red-100 dark:bg-red-900 border-red-500"
data-callout="caution"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-red-600 dark:text-red-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Caution&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;Run these commands as &lt;code&gt;root&lt;/code&gt;, and do not remove the kernel currently running the system. If the target kernel is shown by &lt;code&gt;uname -r&lt;/code&gt;, reboot into another kernel first.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;details class="print:hidden xl:hidden" open&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;div class="text-sm"&gt;
&lt;nav id="TableOfContents"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#confirm-the-running-kernel"&gt;Confirm the running kernel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#list-installed-kernel-packages"&gt;List installed kernel packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#remove-an-old-kernel-on-red-hat-like-systems"&gt;Remove an old kernel on Red Hat-like systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#remove-an-old-kernel-on-debian-like-systems"&gt;Remove an old kernel on Debian-like systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#do-a-final-sanity-check"&gt;Do a final sanity check&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#limit-future-build-up"&gt;Limit future build-up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#keep-the-habit-conservative"&gt;Keep the habit conservative&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;div class="hb-steps"&gt;
&lt;h3 id="confirm-the-running-kernel"&gt;Confirm the running kernel&lt;/h3&gt;
&lt;p&gt;Start with the one kernel version that must not be removed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uname -r
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this prints the version you planned to delete, stop here. Reboot, select a newer or otherwise known-good kernel from GRUB, then check again.&lt;/p&gt;
&lt;h3 id="list-installed-kernel-packages"&gt;List installed kernel packages&lt;/h3&gt;
&lt;p&gt;On Red Hat-like systems such as RHEL, CentOS Stream, Rocky Linux, AlmaLinux, or Fedora, query RPM packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rpm -aq &lt;span class="p"&gt;|&lt;/span&gt; grep kernel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On Debian-like systems such as Debian or Ubuntu, query installed &lt;code&gt;linux-*&lt;/code&gt; packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -l &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;linux-image|linux-headers|linux-modules&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;^ii&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What you are looking for is the version suffix shared by the packages that belong to the old kernel. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;6.8.5-301.fc40.x86_64&lt;/code&gt; on a Fedora-style system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;5.4.0-165-generic&lt;/code&gt; on an Ubuntu-style system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I usually copy the suffix into a variable and print the matched packages before removing anything. It adds one boring step, and boring is excellent here.&lt;/p&gt;
&lt;h3 id="remove-an-old-kernel-on-red-hat-like-systems"&gt;Remove an old kernel on Red Hat-like systems&lt;/h3&gt;
&lt;p&gt;Use the package manager for the package removal itself, then clean the leftover modules directory if it remains.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;6.8.5-301.fc40.x86_64&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;uname -r&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Refusing to remove the running kernel: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rpm -aq &lt;span class="p"&gt;|&lt;/span&gt; grep kernel &lt;span class="p"&gt;|&lt;/span&gt; grep -F &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rpm -aq &lt;span class="p"&gt;|&lt;/span&gt; grep kernel &lt;span class="p"&gt;|&lt;/span&gt; grep -F &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs -r dnf remove -y
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf &lt;span class="s2"&gt;&amp;#34;/lib/modules/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dnf autoremove
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If your system only has &lt;code&gt;yum&lt;/code&gt;, use it for the final clean-up step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yum autoremove
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then refresh GRUB configuration files under &lt;code&gt;/boot&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls -l /boot/ &lt;span class="p"&gt;|&lt;/span&gt; grep rescue
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find /boot/ -name &lt;span class="s1"&gt;&amp;#39;grub.cfg&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Updating &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; grub2-mkconfig -o &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some systems also keep Boot Loader Specification entries under &lt;code&gt;/boot/loader/entries/&lt;/code&gt;. If the removed kernel still has a stale &lt;code&gt;.conf&lt;/code&gt; file there, remove that specific file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls -l /boot/loader/entries/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Be picky here. Delete only entries that clearly match the kernel version already removed.&lt;/p&gt;
&lt;h3 id="remove-an-old-kernel-on-debian-like-systems"&gt;Remove an old kernel on Debian-like systems&lt;/h3&gt;
&lt;p&gt;Again, set the version suffix first. &lt;code&gt;apt purge&lt;/code&gt; removes the matching kernel packages and their package configuration; the separate &lt;code&gt;apt autoremove&lt;/code&gt; then lets APT clean up dependencies that are no longer needed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;5.4.0-165-generic&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;uname -r&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Refusing to remove the running kernel: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -l &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;linux-image|linux-headers|linux-modules&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;^ii&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;$&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -l &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;linux-image|linux-headers|linux-modules&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;^ii&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;$&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs -r apt purge -y
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf &lt;span class="s2"&gt;&amp;#34;/lib/modules/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;deprecated_kernel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt autoremove
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;update-grub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On older Ubuntu releases, this pattern is especially handy after a manual kernel update: headers, image packages, modules, and modules-extra packages may all share the same version suffix.&lt;/p&gt;
&lt;h3 id="do-a-final-sanity-check"&gt;Do a final sanity check&lt;/h3&gt;
&lt;p&gt;After clean-up, inspect what remains:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uname -r
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Red Hat-like&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rpm -aq &lt;span class="p"&gt;|&lt;/span&gt; grep kernel
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Debian-like&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -l &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;linux-image|linux-headers|linux-modules&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;^ii&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result I want is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The running kernel is still installed.&lt;/li&gt;
&lt;li&gt;At least one known-good fallback kernel remains, unless this is a tightly controlled image-building environment.&lt;/li&gt;
&lt;li&gt;GRUB no longer shows entries for kernel packages that were already removed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/lib/modules/&lt;/code&gt; does not contain a directory for the removed kernel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="limit-future-build-up"&gt;Limit future build-up&lt;/h3&gt;
&lt;p&gt;On Fedora/RHEL-like systems, DNF normally keeps only a small number of install-only packages, including kernels. If the host keeps too many kernels, check the configured limit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -E &lt;span class="s1"&gt;&amp;#39;^installonly_limit=&amp;#39;&lt;/span&gt; /etc/dnf/dnf.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For most ordinary hosts, &lt;code&gt;installonly_limit=3&lt;/code&gt; is a reasonable balance: current kernel, one fallback, and one extra slot during updates. I would avoid setting it below &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On Debian/Ubuntu, old automatically installed kernels are often handled by:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt autoremove
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If a kernel package was manually marked as installed, APT may keep it. In that case, inspect the package names first and only then adjust the manual/auto mark or purge the exact old packages.&lt;/p&gt;
&lt;h3 id="keep-the-habit-conservative"&gt;Keep the habit conservative&lt;/h3&gt;
&lt;p&gt;Kernel clean-up is not about being tidy for its own sake. It is about keeping the boot path understandable while preserving a rollback option.&lt;/p&gt;
&lt;p&gt;For normal servers, I prefer keeping the current kernel and one previous known-good kernel. For disposable build hosts or image-making VMs, I may be more aggressive, but only after the new kernel has booted and the important workload has actually run.&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>Disabling FaceFusion's NSFW Filter from Source Code</title><link>https://www.isarvin.com/blog/disable-facefusion-nsfw-filter/</link><pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate><guid>https://www.isarvin.com/blog/disable-facefusion-nsfw-filter/</guid><description>&lt;p&gt;FaceFusion 3.7.0 has a built-in content analyser. In the source I checked, image and video workflows call it before doing the main processing work. If the analyser says the target is NSFW, the workflow returns early.&lt;/p&gt;
&lt;p&gt;This blog records a tiny local patch to disable that gate from source code.&lt;/p&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-red-100 dark:bg-red-900 border-red-500"
data-callout="caution"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-red-600 dark:text-red-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Caution&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;I only do this for research on local model behaviour and source-code analysis, not for anything illegal, non-consensual, or prohibited for anyone under 18. In short: lab coat on, age gate on, common sense on. The lab coat may be imaginary; the age gate is not. 🧪&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="note"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8l.8-2.685a4.5 4.5 0 0 1 1.13-1.897zm0 0L19.5 7.125"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Note&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;The examples below are based on FaceFusion 3.7.0, from a &lt;code&gt;master&lt;/code&gt; checkout at &lt;code&gt;b8f8046 last minute change to frame distribution&lt;/code&gt;. I have decided not to chase every upstream change here, but the same idea should theoretically apply to FaceFusion 3.5.x through 3.8.x; at the time of writing, the latest release I checked is 3.8.2. If you prefer a more detailed guide, see
.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;details class="print:hidden xl:hidden" open&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;div class="text-sm"&gt;
&lt;nav id="TableOfContents"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#find-where-facefusion-checks-content"&gt;Find where FaceFusion checks content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#make-detect_nsfw-always-return-false"&gt;Make &lt;code&gt;detect_nsfw()&lt;/code&gt; always return &lt;code&gt;False&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#remove-the-content-analyser-hash-guard"&gt;Remove the content analyser hash guard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#review-the-final-diff"&gt;Review the final diff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#verify-the-patch-locally"&gt;Verify the patch locally&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#keep-the-patch-local"&gt;Keep the patch local&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;div class="hb-steps"&gt;
&lt;h3 id="find-where-facefusion-checks-content"&gt;Find where FaceFusion checks content&lt;/h3&gt;
&lt;p&gt;In the image workflow, setup calls &lt;code&gt;analyse_image()&lt;/code&gt; before creating temporary files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;analyse_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target_path&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The video workflow does the same with &lt;code&gt;analyse_video()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;trim_frame_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trim_frame_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restrict_trim_frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target_path&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;state_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trim_frame_start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;state_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;trim_frame_end&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;analyse_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target_path&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;trim_frame_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trim_frame_end&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both paths eventually land in &lt;code&gt;facefusion/content_analyser.py&lt;/code&gt;. For a single frame, the call chain is short:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyse_frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VisionFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;detect_nsfw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the practical switch is &lt;code&gt;detect_nsfw()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="make-detect_nsfw-always-return-false"&gt;Make &lt;code&gt;detect_nsfw()&lt;/code&gt; always return &lt;code&gt;False&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Originally, &lt;code&gt;detect_nsfw()&lt;/code&gt; combined three detector results and returned true when at least two detectors agreed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;detect_nsfw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VisionFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is_nsfw_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detect_with_nsfw_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is_nsfw_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detect_with_nsfw_2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is_nsfw_3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detect_with_nsfw_3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_nsfw_1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_nsfw_2&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;is_nsfw_1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_nsfw_3&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;is_nsfw_2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_nsfw_3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For my local research checkout, I changed it to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;detect_nsfw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_frame&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VisionFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is intentionally blunt. &lt;code&gt;analyse_image()&lt;/code&gt; and &lt;code&gt;analyse_video()&lt;/code&gt; still run, but the underlying frame result is always &amp;ldquo;not NSFW&amp;rdquo;, so the early &lt;code&gt;return 3&lt;/code&gt; branch is not triggered by this analyser.&lt;/p&gt;
&lt;h3 id="remove-the-content-analyser-hash-guard"&gt;Remove the content analyser hash guard&lt;/h3&gt;
&lt;p&gt;There is one more check in &lt;code&gt;facefusion/core.py&lt;/code&gt;. During &lt;code&gt;common_pre_check()&lt;/code&gt;, FaceFusion hashes the source of the &lt;code&gt;content_analyser&lt;/code&gt; module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;content_analyser_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_analyser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash_helper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_analyser_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;975d67d6&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After editing &lt;code&gt;content_analyser.py&lt;/code&gt;, that hash no longer matches. So the second local change is to stop enforcing the original hash:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result is deliberately small: &lt;code&gt;common_pre_check()&lt;/code&gt; becomes a plain pass-through for this local checkout. If I were maintaining a long-lived fork, I would replace this with a proper configuration flag.&lt;/p&gt;
&lt;h3 id="review-the-final-diff"&gt;Review the final diff&lt;/h3&gt;
&lt;p&gt;The whole patch is small:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/facefusion/content_analyser.py b/facefusion/content_analyser.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; def detect_nsfw(vision_frame : VisionFrame) -&amp;gt; bool:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- is_nsfw_1 = detect_with_nsfw_1(vision_frame)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- is_nsfw_2 = detect_with_nsfw_2(vision_frame)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- is_nsfw_3 = detect_with_nsfw_3(vision_frame)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- return is_nsfw_1 and is_nsfw_2 or is_nsfw_1 and is_nsfw_3 or is_nsfw_2 and is_nsfw_3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ return False
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/facefusion/core.py b/facefusion/core.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- content_analyser_content = inspect.getsource(content_analyser).encode()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- return hash_helper.create_hash(content_analyser_content) == &amp;#39;975d67d6&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ return True
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="verify-the-patch-locally"&gt;Verify the patch locally&lt;/h3&gt;
&lt;p&gt;First, confirm only the intended files changed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git -C /path/to/facefusion diff --stat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git -C /path/to/facefusion diff -- facefusion/content_analyser.py facefusion/core.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then run FaceFusion from that modified checkout and retry the input that previously stopped at the content analyser gate.&lt;/p&gt;
&lt;p&gt;For a quick source-level sanity check, I also like searching the call sites:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -n &lt;span class="s2"&gt;&amp;#34;analyse_image|analyse_video|detect_nsfw|common_pre_check&amp;#34;&lt;/span&gt; facefusion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If &lt;code&gt;rg&lt;/code&gt; is not available, use plain &lt;code&gt;grep&lt;/code&gt; instead:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -RInE &lt;span class="s2"&gt;&amp;#34;analyse_image|analyse_video|detect_nsfw|common_pre_check&amp;#34;&lt;/span&gt; facefusion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my checkout, the important shape is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;image_to_image.py&lt;/code&gt; calls &lt;code&gt;analyse_image()&lt;/code&gt; during setup.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image_to_video.py&lt;/code&gt; calls &lt;code&gt;analyse_video()&lt;/code&gt; during setup.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;analyse_frame()&lt;/code&gt; delegates to &lt;code&gt;detect_nsfw()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;common_pre_check()&lt;/code&gt; returns &lt;code&gt;True&lt;/code&gt; instead of rejecting the edited analyser source by hash.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="keep-the-patch-local"&gt;Keep the patch local&lt;/h3&gt;
&lt;p&gt;This is the kind of modification I would keep as a local research patch, not something to casually publish as a default behaviour change. Filters can be annoying when they block legitimate experiments, but they also exist because image-generation and face-processing tools can be abused very easily.&lt;/p&gt;
&lt;p&gt;So yes, this is how I disabled the gate in source code. No, this is not a permission slip for creepy things. If the target is non-consensual, illegal, or involves anyone under 18, the correct patch is not in Python. It is in the user&amp;rsquo;s judgement. Please upgrade that dependency first. 🧠&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>Building PyPy 3 from Source on a LoongArch64 Platform</title><link>https://www.isarvin.com/blog/pypy3-for-loongarch64/</link><pubDate>Sun, 23 Feb 2025 00:00:00 +0000</pubDate><guid>https://www.isarvin.com/blog/pypy3-for-loongarch64/</guid><description>&lt;p&gt;
provides general guidance on building from source. Since PyPy does not officially support Linux LoongArch64, I would like to share my own process for building a binary for this relatively new architecture.&lt;/p&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="note"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8l.8-2.685a4.5 4.5 0 0 1 1.13-1.897zm0 0L19.5 7.125"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Note&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;ol&gt;
&lt;li&gt;The following is demonstrated as the root user in a LoongArch64 VM running Kylin Server V10 SP3. Adjustments may be required to match your environment.&lt;/li&gt;
&lt;li&gt;PyPy 3.11 v7.3.18 is used. Other PyPy 3 versions should follow a similar process.&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In particular, I should stress that this OS version leverages old-world&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; technologies.&lt;/p&gt;
&lt;details class="print:hidden xl:hidden" open&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;div class="text-sm"&gt;
&lt;nav id="TableOfContents"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#prerequisites-python-27-and-at-least-6-gb-memory-for-translation"&gt;Prerequisites: Python 2.7 and at least 6 GB memory for translation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#get-the-source"&gt;Get the source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#install-build-time-dependencies"&gt;Install build-time dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#translate-without-jit"&gt;Translate without JIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#packaging"&gt;Packaging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;div class="hb-steps"&gt;
&lt;h3 id="prerequisites-python-27-and-at-least-6-gb-memory-for-translation"&gt;Prerequisites: Python 2.7 and at least 6 GB memory for translation&lt;/h3&gt;
&lt;p&gt;As the official documentation states, the translation process is indeed RAM-hungry. So please take it seriously.&lt;/p&gt;
&lt;p&gt;The documentation also mentions that CPython/PyPy 2.7 is required. Building PyPy 2.7 for LoongArch64 is probably not worth the effort here, so I&amp;rsquo;ll just install Python 2.7 in my VM.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dnf install -y python2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# command -v python2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="get-the-source"&gt;Get the source&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://downloads.python.org/pypy/pypy3.11-v7.3.18-src.tar.bz2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tar -xvf pypy3.11-v7.3.18-src.tar.bz2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# rm -f pypy3.11-v7.3.18-src.tar.bz2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Apply patches here.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-purple-100 dark:bg-purple-900 border-purple-500"
data-callout="important"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-purple-600 dark:text-purple-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0a9 9 0 0 1 18 0m-9 3.75h.008v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Important&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;Patches must be applied to support LoongArch64. See
for an example.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="install-build-time-dependencies"&gt;Install build-time dependencies&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dnf install -y bzip2-devel expat-devel gc-devel gcc gdbm-devel libffi-devel make ncurses-devel openssl-devel pkgconfig python2-pip sqlite-devel tk-devel xz-devel zlib-devel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CFFI is required. I suggest using a Python 2.7 virtual environment.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# python2 -m pip -V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python2 -m pip install virtualenv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python2 -m virtualenv venv_2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; venv_2/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# command -v python&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# python -m pip -V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -m pip install cffi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="translate-without-jit"&gt;Translate without JIT&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; pypy3.11-v7.3.18-src/pypy/goal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python ../../rpython/bin/rpython --opt&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deactivate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It can be time-consuming. Please be patient. A successful build should end with output like this.&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="FYI, the end of a successful build."
srcset="https://www.isarvin.com/blog/pypy3-for-loongarch64/end-of-build_hu_82d7fd53879dcb0.webp 320w, https://www.isarvin.com/blog/pypy3-for-loongarch64/end-of-build_hu_b7d10d68b7064224.webp 480w, https://www.isarvin.com/blog/pypy3-for-loongarch64/end-of-build_hu_509c86c8d062545.webp 724w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://www.isarvin.com/blog/pypy3-for-loongarch64/end-of-build_hu_82d7fd53879dcb0.webp"
width="724"
height="291"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;After a successful build, you can also play with the compiled binary.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./pypy3.11-c -V
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./pypy3.11-c -c &lt;span class="s1"&gt;&amp;#39;print(&amp;#34;Hello World!&amp;#34;)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./pypy3.11-c -m pip -V
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="packaging"&gt;Packaging&lt;/h3&gt;
&lt;p&gt;I will use the compiled binary directly for packaging.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./pypy3.11-c ../../pypy/tool/release/package.py --help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./pypy3.11-c ../../pypy/tool/release/package.py --archive-name&lt;span class="o"&gt;=&lt;/span&gt;pypy3.11-v7.3.18-loongarch64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;This creates a clean and prepared hierarchy, as well as a &lt;code&gt;.tar.bz2&lt;/code&gt; with the same content; the directory to find these will be printed out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;See
for a brief intro on the so-called old/new world.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>A Starter for Python 2 Code Formatting with Black</title><link>https://www.isarvin.com/blog/black-for-py2.7/</link><pubDate>Sun, 09 Feb 2025 00:00:00 +0000</pubDate><guid>https://www.isarvin.com/blog/black-for-py2.7/</guid><description>
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-red-100 dark:bg-red-900 border-red-500"
data-callout="caution"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-red-600 dark:text-red-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Caution&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;Python 2.7 is the last major version in the 2.x series and
. Please upgrade your Python if possible.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Some of the projects I work on still use Python 2.7. Installing Black for that case can take a little fiddling, so I&amp;rsquo;d like to show the steps that worked for me.&lt;/p&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="note"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8l.8-2.685a4.5 4.5 0 0 1 1.13-1.897zm0 0L19.5 7.125"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;Note&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;ol&gt;
&lt;li&gt;
, even for formatting Python 2 code.&lt;/li&gt;
&lt;li&gt;The following assumes you have admin/root privileges and use &lt;em&gt;pip&lt;/em&gt; as your package installer. Adjustments may be required to match your environment.&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;details class="print:hidden xl:hidden" open&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;div class="text-sm"&gt;
&lt;nav id="TableOfContents"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#prerequisites-python-36-with-a-package-installer"&gt;Prerequisites: Python 3.6+ with a package installer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#install-a-specific-black-version"&gt;Install a specific Black version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#verify-installation"&gt;Verify installation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;div class="hb-steps"&gt;
&lt;h3 id="prerequisites-python-36-with-a-package-installer"&gt;Prerequisites: Python 3.6+ with a package installer&lt;/h3&gt;
&lt;p&gt;I am not going to cover how to install Python 3 or set up a package installer such as &lt;em&gt;pip&lt;/em&gt;. You may need to search the web if your environment does not meet the prerequisites.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;command&lt;/span&gt; -v python3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m pip -V
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="install-a-specific-black-version"&gt;Install a specific Black version&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m pip install &lt;span class="s1"&gt;&amp;#39;black[python2]==21.12b0&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;click&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;8.0.4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;V21.12b0 is the last Black version with support for formatting Python 2 code. As defined in
, &lt;code&gt;click &amp;gt;= 7.1.2&lt;/code&gt; is a must.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;click &amp;lt;= 8.0.4&lt;/code&gt; is also required. Otherwise, you may encounter an error saying &lt;code&gt;ImportError: cannot import name '_unicodefun' from 'click'&lt;/code&gt; when running Black.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;python2&lt;/code&gt; extra for Black installs the optional dependency &lt;em&gt;typed-ast&lt;/em&gt;, which
. If there is no matching pre-built binary package for your environment, building it can fail with a Clang or GCC error. My suggestion is to remove &lt;code&gt;[python2]&lt;/code&gt; as shown below. It generally works fine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m pip install &lt;span class="nv"&gt;black&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;21.12b0 &lt;span class="nv"&gt;click&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;8.0.4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="verify-installation"&gt;Verify installation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m black --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</description></item></channel></rss>