A RISC-V hypervisor in slow motion: XCP-ng and Xen Orchestra, from inside QEMU

Photo by John Thomas on Unsplash.
In November 2025, at KubeCon in Atlanta, we showed XCP-ng on Ampere: an Ampere Altra-based System76 Thelio Astra running XCP-ng, managed by Xen Orchestra. A real Arm machine, doing real hypervisor things, in front of real people.
So in October we set ourselves the same goal on RISC-V. (One new architecture a year is clearly not enough for some of us, and by "some of us" I mostly mean me.) The target was a host that Xen Orchestra can manage, and a guest on it running containers: Docker first, then Kubernetes with K3s.
Did we get there?
In QEMU, yes. Xen Orchestra lists the RISC-V host and its VMs, opens their consoles, shows their stats, starts and stops them. A Debian guest on that host runs Docker, and K3s reports Ready about a minute after starting. And as a bonus, the same guest shows an XFCE desktop in Xen Orchestra’s console tab.
Now, before anyone gets excited and starts shopping for boards: unlike the Arm demo, all of this runs in QEMU, emulated on an x86 machine. Since early October, that x86 machine has been pretending to be a RISC-V machine, with a lot of dedication and very little speed. We have not run this full stack on a RISC-V board yet. This is a prototype, research and development in the wild, and what follows is a progress report rather than an announcement (I’m repeating that to myself as much as to you, because my enthusiasm has a habit of slipping its leash).
The people who did the hard part first
None of what follows exists without these people, so they come first.
Baptiste Le Duc and Oleksii Kurochko have been porting the Xen hypervisor itself to RISC-V for a while now, and the xcp-ng.org blog has followed them: Xen booted a guest on real RISC-V hardware in September. Then Zheng Zhang (ISCAS, the Institute of Software, Chinese Academy of Sciences) took those patches and booted Xen on his own SpacemiT K3, the first RVA23 board Xen runs on.
The original version of this story on xcp-ng.org was written together with Baptiste, Yann Sionneau and Julian Vetter, so their names belong up here too.
So where does my October fit in?
A hypervisor is not a virtualization platform. XCP-ng is Xen plus a dom0 with its whole package set, plus XAPI, the toolstack that Xen Orchestra talks to. Baptiste’s trees already carry the RISC-V Xen, the RISC-V toolstack (XAPI, xenopsd, xenguest), the dom0 kernel, and the dom0 image build. Since early October, we have been working on the layer around them: package that stack for riscv64, boot it, fix what breaks along the way, and get Xen Orchestra to manage the result. The unglamorous plumbing, in other words, which happens to be my favourite kind.
A word on method, because I’d rather say it now than have someone find out later: I built much of this with heavy help from AI coding assistants, so expect parts of it to change as the patches go through upstream review.
What works today
The stack runs in QEMU with 3 harts and 5 GiB of RAM. The dom0 is AlmaLinux Kitten running the riscv64 RPMs of our latest build round, round 19, Xen and the dom0 kernel included. Xen Orchestra 6.9.1 is a riscv64 build of its own (gounthar/xen-orchestra-riscv64). On October 20th, on that round, it gave:
the host and its VMs in the inventory
host and VM consoles
host and VM stats (the graphs are in the XO 5 interface, which XO 6 links to)
start, clean shutdown, force shutdown, and start again
a Debian 13 (trixie) guest with a disk on a storage repository and a network that reaches the internet
an XFCE desktop in the VM’s console tab
That fourth line looks boring. It wasn’t, and I’ll come back to it.

Figure 1. A Debian 13 RISC-V guest booting to a login prompt, in the Xen Orchestra 6 console.

Figure 2. CPU, memory, network, and disk statistics for the same guest, in Xen Orchestra 5.
Inside that guest, on a kernel built with the container options, Docker runs riscv64 containers and K3s reports Ready.
Is it polished? Not quite. On one start, Xen Orchestra showed no IP address for the guest for more than five minutes, although the guest had its lease after 24 seconds. Xen Orchestra reports the guest’s disk as 1 GiB when the guest sees 8 GiB (a rounding error of a mere factor of eight), and Docker pulls sometimes need a retry.
The packages are public, as experimental and unsigned releases in gounthar/xcpng-riscv64-rpms: 19 build rounds so far, published from round 10. They come from Yann Dirson's meta-xcpng BitBake layer, with a riscv64 port of ours on top that is not part of his layer. (Yann Dirson, not to be confused with Yann Sionneau above. Yes, there are two Yanns in this story. No, it doesn’t get easier.)
In a riscv64 AlmaLinux Kitten container, with the Kitten repositories and the latest round, this resolves and installs:
dnf install xen-hypervisor xen-tools kernel-xcpng-riscv64 xcp-networkd xcp-ng-release-configLet me be careful about what that means, because it’s easy to read too much into a green dnf run. It proves the packaging, not that the stack runs. And like the Xen Orchestra build, these packages are a personal, unsupported, and not production-ready project. Please don’t put them anywhere near anything you care about.
Starting a VM twice in one boot
Remember that boring-looking line about "start again"?
For the first few days, we could start exactly one VM per boot. Shut it down, and the next start never ran. One VM per boot is not so much a virtualization platform as a very elaborate bootloader.
The dead guest was never freed, and it took a fix on each side. In Xen, the RISC-V functions that release a dead domain’s resources were still stubs, and foreign mappings never dropped the page reference they take. In the dom0 kernel, the function that should unmap a guest’s pages returned success without telling Xen anything, so about 28 MB of every dead guest stayed pinned. (A function that reports success without doing the work: we’ve all worked with one, and some of us have written one.)
With both fixes, we ran three start, shutdown, start cycles in a row, and free memory came back to its baseline each time. Both fixes are merge requests on Baptiste’s trees, waiting for his review.
Getting the guest on the network
No network, no container images, so this one stood squarely between me and Docker. The first networked guest gave up right away:
grant table add_to_physmap failed, err=-38-38 is ENOSYS (the kernel’s polite way of saying "never heard of it"). Xen on RISC-V did not yet implement the call a guest uses to grow its grant table, and a network frontend needs 512 grants to start. The Arm port has that case; RISC-V did not.
We expected one patch and needed three: the missing case in Xen, a scratch page for grant seeding in libxenguest, and an init function in the RISC-V Linux port that returned an error and stopped grant table setup halfway. (Expect one, get three. If you’ve done any porting work, you’re nodding right now.) After those, the guest was able to ping the dom0, and later reached the internet. These are also merge requests on Baptiste’s trees, waiting for review.
A desktop in the console tab
Both consoles in Xen Orchestra (XO) were text-only at first. Perfectly respectable, and perfectly black and white.
Julian Vetter had already solved this for the Arm port, so we borrowed his approach: QEMU runs in dom0 only as a Xen PV backend, the guest uses Xen’s PV framebuffer, and Xen Orchestra connects to it like any other console.
On RISC-V that meant building a riscv64 QEMU from his XCP-ng QEMU branch plus two RISC-V patches, teaching xenopsd’s RISC-V builder to start it, and enabling the keyboard and mouse frontend in the guest kernel. Then one setting on the VM:
platform:vfb=trueAnd XFCE shows up in the console tab.

Figure 3. An XFCE desktop on a RISC-V guest, in the Xen Orchestra 6 console tab. uname -a reports riscv64.
I know a desktop is not what anyone runs a hypervisor for. It does make a better demo than a login prompt.
And once there’s a desktop, you know what has to run on it. On October 6th, Chocolate Doom 3.1.0 ran in that same console tab, with the Freedoom Phase 1 game data (not id’s WAD), both straight from Debian trixie’s riscv64 repositories. Installing the two packages took 6 minutes 39 seconds under emulation.
The console’s first trick was to sit on its loading logo forever. Xen Orchestra reaches a VM’s console through XAPI’s websocket proxy, and that proxy only accepted sockets named vnc-N, while the PV framebuffer’s QEMU names its socket pv-vnc-N. Once XAPI accepted that name too (build round 13), the logo gave way to the desktop. Then the mouse pointer kept drifting to the right, because QEMU doesn’t tell the guest’s virtual keyboard driver how big the screen is. Telling the guest kernel directly fixed it:
xen_kbdfront.ptr_size=1280,800
Figure 4. Freedoom on a RISC-V guest, in the Xen Orchestra 6 console tab, on October 6th, on an earlier build round.
The same run on video, recorded on 2026-10-06.
Is it playable? I honestly don’t know: nobody measured the frame rate, and under emulation I wouldn’t bet on it. But it runs, and some traditions have to be respected.
One fix that came from upstream
For a while, Xen Orchestra would lose the RISC-V pool every time XAPI was restarted, and only a restart of xo-server would bring it back.
That one, we didn’t have to fix ourselves: Xen Orchestra 6.9.0 fixed it. Since we updated, xo-server reconnects on its own, about 20 seconds after a XAPI restart. (I’d love to take credit for that one. I can’t. All I did was update.)
What emulation costs
QEMU emulating RISC-V on x86 is slow, and it shows.
That minute for K3s is emulation time. A guest reads its disk at about 800 KB/s. Yes, kilobytes, per second, and I’ll let that sink in slowly, at roughly the speed of the disk. A clean shutdown takes about 20 seconds. The packages are built under emulation too: the OCaml build alone takes 72 minutes (plenty of time to reconsider a few life choices).
None of those figures say anything about how RISC-V hardware performs. They only explain why we want to switch to using a real hardware board.
What’s next
Three directions, none of them dated (on purpose: a date in a blog post reads like a promise, and this is a prototype):
Real RISC-V hardware. Our boot path still assumes QEMU’s virtual machine layout. Booting the full stack on a board, as our Arm demo did on Ampere, is the next milestone.
Upstreaming. Our patches go to Baptiste’s Xen, Linux, and toolstack trees first, for review, then upstream from there. Some are merged in his trees; most are still waiting.
Reproducible package builds. Native builds on a RISC-V board instead of emulation, and a riscv64 port that fits meta-xcpng cleanly.
How to help
Don’t have a RISC-V board? This stack hasn’t met one yet either, so you’re in good company.
You don’t need one to contribute. Start with the packages: in a riscv64 AlmaLinux Kitten container, download the latest round and install it as a local repository, as the repository’s README shows, and run the install command above. That only proves they install, but a dependency that does not resolve or a file in the wrong place is a bug, and telling us about it on the forum is already a contribution.
If you know RISC-V, virtualization, or packaging, you are welcome to review Baptiste’s Xen tree, Linux tree, and xen-api fork. (Yes, this is me asking for reviewers again. I’m told persistence is a feature.)
What I’m taking away from October
An emulator number is an emulator number: 800 KB/s disk reads say something about QEMU on x86 and nothing about RISC-V silicon. When RISC-V is missing something, look at the Arm port first, because the grant table case was already there, and so was Julian’s console approach. Budget for three patches when you expect one. Update before you debug, since the XAPI reconnect problem had been fixed in Xen Orchestra 6.9.0 all along, waiting for me to notice. And a package that installs is not a stack that runs.
This is my personal retelling of a post published on the XCP-ng blog, written with a bit more mischief than the house voice allows. The original, co-authored with Baptiste Le Duc, Yann Sionneau and Julian Vetter, lives on xcp-ng.org: XCP-ng and Xen Orchestra on RISC-V: a progress report from the emulator.