Skip to content

About

Run the official OpenHarmony (Oniro) emulator userland, byte-for-byte unmodified, inside a runc container on an Alpine Linux guest — stock QEMU, one 'make run'.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

harmonypod

Run the official OpenHarmony emulator userland — byte-for-byte unmodified — inside a runc container on a small Alpine Linux guest under stock QEMU, and get the full OpenHarmony UI (lock screen, SystemUI, launcher) rendering to a VNC framebuffer.

host QEMU (any distro build, 2D virtio-gpu — no GL, no patches)
 └─ Alpine 3.20 guest   (PID 1 = /sbin/sshinit: cgroup v1 with six controllers,
 │                        bind-mounts /dev/{cpuset,memcg,pids}, sshd)
     └─ runc container  (rootfs = the OpenHarmony userland, PID 1 = OHOS /init)
          → composer_host / render_service / systemui / launcher all start
          → lock screen renders via DRM scanout through virtio-gpu
          → watch it on VNC, verify it with the colour-fingerprint check

Oniro (the OpenHarmony distribution by the Oniro Project) ships an x86_64 emulator image that normally boots under a patched QEMU with an OpenHarmony-tuned kernel. harmonypod takes a different route: the image's userland only is packed into an ext4 image, mounted inside a plain Alpine guest, and started with plain runc run. Nothing inside the OpenHarmony userland is patched, rebuilt or replaced — including the graphics HAL. The composer talks to the virtual GPU through the kernel DRM interface and scans out directly, which is why a 2D virtio-gpu-pci is enough.

Zero modification — verify it yourself

The OHOS rootfs image (work/ohos-rootfs.img) is assembled by ohos-rootfs/build.sh purely by concatenating the two ext4 images from the official oniro_emulator.zip:

  • images/system.img — already a rootfs-style ext4 image (system/, vendor/, usr/, bin -> /system/bin, ... at the top level). It is copied to the new image as the whole root.
  • images/vendor.img — overlays the root's vendor/ directory.

The build then checks md5 anchors of the graphics stack (and fails if they mismatch or if libwayland-client.so.0 is present — it is not, in the pristine image):

file (inside the rootfs) md5 (first 8)
vendor/lib64/libdisplay_composer_vendor.z.so 483714b6
vendor/lib64/passthrough/libdisplay_buffer_vdi_impl.z.so 5bccd321
system/etc/init/graphic.cfg 1efe293d
system/bin/render_service 1e58c3c4

Re-check from a running guest any time:

$ md5sum /mnt/ohos/vendor/lib64/libdisplay_composer_vendor.z.so \
         /mnt/ohos/vendor/lib64/passthrough/libdisplay_buffer_vdi_impl.z.so \
         /mnt/ohos/system/etc/init/graphic.cfg /mnt/ohos/system/bin/render_service

Prerequisites

  • Linux x86_64 host with qemu-system-x86_64 ≥ 8 (distro package is fine — the 2D path needs no GL and no patched QEMU), python3, gcc + libdrm headers, e2fsprogs (mkfs.ext4), curl, tar.
  • KVM strongly recommended (/dev/kvm writable); without it the guest falls back to TCG and everything is slow.
  • Root for loop-device mounts and mkfs (or run inside a privileged container).
  • oniro_emulator.zip — the Oniro Project x86_64 emulator bundle (contains images/system.img + images/vendor.img). See the Oniro documentation for the current download location. Put the path in config/env.
  • The guest kernel bzImage — either download bzImage-container from this project's GitHub Releases and place it at artifacts/bzImage-container, or build your own (make kernel, see below).
  • ~12 GB of free disk (zip extraction + two 4 GB images).

Quick start

$ git clone <this repo> && cd harmonypod
$ cp config/env.example config/env
$ editor config/env              # set ONIRO_ZIP=...
$ ls artifacts/bzImage-container # download the Release bzImage here (or: make kernel)

$ make ohos-rootfs               # assemble work/ohos-rootfs.img + anchor check
$ make guest-rootfs              # build work/alpine-rootfs.img, generate work/guest-key(.pub)
$ make run                       # boot QEMU, wait for ssh, start the OHOS container
$ make accept                    # VNC frame grab + colour check → ACCEPT-PASS/FAIL

make run boots the guest (2D virtio-gpu-pci, two virtio disks, ssh forwarded to 127.0.0.1:55557, VNC on 127.0.0.1:5902), waits for the guest sshd, mounts the OHOS disk at /mnt/ohos, runs the one-shot modeset, and starts runc run ohos. After ~60 s the lock screen is live; make accept prints e.g.

VNC 368x720 distinct=9053 samples=29520
top: [('bdbec2', 1054), ('000000', 720), ('bebdc2', 539), ('bfbec3', 435)]
  bdbec2: 1054 (3.570%)
  ...
ACCEPT-PASS

Open a VNC viewer on 127.0.0.1:5902 (no password) to watch the UI. make bringup restarts the container in a running guest; make clean removes work/.

Status

capability state notes
display, 2D path (DRM direct scanout, zero-mod userland) works this repo's main line; verified with ACCEPT-PASS (distinct ≥ 9000, lock-screen fingerprint ~3.6%) on a stock distro QEMU 8.2.2
display, GPU path (virgl + DRM direct scanout) works separate setup, see gpu/README.md; screenshot docs/screenshots/vdrm-srv11.png
display, GPU path (virgl + wayland + dmabuf zero-copy) works OHOS display HAL wayland backend with zwp_linux_dmabuf_v1; screenshot docs/screenshots/verify-dmabuf.png
hdc (host ↔ device tooling) works connect recipe in tools/build-hdc.md
input (touch / mouse / unlock) known issue events reach the OHOS input stack but are dropped; see docs/STATUS.md — this image can be watched, not operated
audio untested no CI coverage yet
cgroup v2 not supported the recipe mounts cgroup v1 with six controllers; v2 port is on the roadmap

See docs/STATUS.md for the full matrix, environment details and known-issue analysis.

How it works

Guest init (guest/sshinit)

The guest's PID 1 is a small ash script (kernel cmdline init=/sbin/sshinit). It mounts proc/sys/devtmpfs/devpts, sets up slirp networking (10.0.2.15), mounts cgroup v1 with the six controllers the OpenHarmony init expects (pids cpuset memory freezer devices cpu,cpuacct) and bind-mounts them under /dev/{cpuset,memcg,pids} — the runc spec bind-mounts /config from /dev/cpuset, which the OHOS services read. Then it starts sshd (root, public-key only) so the host-side scripts can drive the guest.

The runc spec (guest/runc-config.json)

  • root.path = /mnt/ohos — the mounted OHOS disk (the guest image only carries the spec, not the rootfs).
  • args = ["/init"] — the OHOS init runs as container PID 1.
  • Full capability set, maskedPaths/readonlyPaths emptied — the stock image expects a fairly bare-metal environment.
  • Bind mounts: /tmp/wl and /data/local/wl from the guest's /tmp/xdg are only used by the GPU/wayland branch; on the 2D path they stay empty.
  • cgroup v1 namespace layout is provided by the guest's init (above).

fbmaster: the one-shot modeset (tools/fbmaster.c)

When the container starts, no one is DRM master yet, and with no master the kernel never issues SET_SCANOUT to QEMU's virtio-gpu — the host keeps showing whatever scanout state it booted with. fbmaster briefly claims DRM master, allocates a dumb framebuffer and performs one modeset on the virtual output; that "opens" the scanout path. It exits after 5 s, composer_host (inside the container) then takes over as DRM master and the UI appears.

Acceptance criteria (tools/accept.sh)

A frame is pulled via QEMU's VNC (works for both the 2D and GL paths — the monitor's screendump fails with no surface under -display egl-headless,gl=on). The check requires:

  • ≥ 1000 distinct colours over a 1/3-pixel sampling grid (a dead/black screen yields a handful), and
  • one of the lock-screen fingerprint greys bdbec2 / bebdc2 / bfbec3 present at ≥ 0.05 %.

Building the kernel (make kernel)

The released bzImage-container (md5 2966b7f4fad4c72ba93eaeddf138fe1c) is built from:

  • base tree: OpenHarmony kernel_linux_6_6 (a vanilla 6.6 tree does not work — OHOS userland depends on the OHOS binder extensions),
  • board patches from the Oniro device board (device_board_oniro/x86_general): hdf.patch + kernel_patch/*,
  • the board's x86_general_linux_defconfig plus kernel/container-config.fragment (namespaces, cgroup v1 controllers, veth/bridge, ashmem, SELinux stub, DRM_FBDEV_EMULATION=n, HDF off — every entry commented),
  • kernel/patches/0001-virtio-gpu-deduplicate-formats.patch: virtio_gpu_formats[] lists DRM_FORMAT_ARGB8888 twice on little-endian (HOST_ARGB8888 expands to the same fourcc); weston 12 asserts on the duplicate when building its format list from the driver's list. The patch is queued for upstream submission.

Set OHOS_KERNEL_SRC, BOARD_PATCH_DIR, BOARD_DEFCONFIG in config/env, then make kernel.

Known issues & roadmap

  • Input does not work (biggest gap): pointer events reach MMI but get dropped (Failed to obtain physical display, deviceId:4, MF:-1); injection via uitest/uinput is rejected by VerifySystemApp() because the caller has no OHOS bundle identity. Identical on the DRM path ⇒ an OpenHarmony image/input-stack issue, not a container issue. Needs an OHOS-side fix (display-group/device-id resolution). Details in docs/STATUS.md.
  • cgroup v2: the recipe hard-depends on v1 (/config bind from /dev/cpuset, etc.). A v2 port needs OHOS init changes.
  • Bare-metal host variant: running the OHOS userland directly on a host (no VM) additionally needs ashmem (CONFIG_ASHMEM) and binderfs on the host kernel — untested.
  • Upstreaming: the virtio-gpu format dedup patch; cgroup controller requirements documentation.
  • Audio: untested (no audio device is passed through in the 2D recipe).

Repository layout

config/env.example        build/run configuration (copy to config/env)
ohos-rootfs/build.sh      oniro_emulator.zip -> work/ohos-rootfs.img (+ anchor check)
guest/build.sh            alpine-minirootfs -> work/alpine-rootfs.img (+ ssh keypair)
guest/sshinit             guest PID 1 (cgroups, sshd)
guest/runc-config.json    runc OCI spec (root.path=/mnt/ohos)
host/run-2d.sh            QEMU 2D launch + ssh wait
tools/bringup.sh          mount OHOS disk, modeset, runc run
tools/accept.sh           VNC frame grab + colour acceptance
tools/fbmaster.c          one-shot DRM master/modeset helper
tools/vncsnap.py, see.py  minimal RFB client / PPM colour statistics
kernel/                   kernel fragment + patches + build script
gpu/README.md             the GPU (virgl) branch: patches, gotchas, artifacts
docs/STATUS.md            completion matrix + known issues
docs/screenshots/         proof screenshots (2D / virgl+DRM / dmabuf zero-copy)
artifacts/                gitignored; Release binaries (bzImage) live here locally

Nothing binary is committed: images land in work/ (gitignored), prebuilt kernels and the GPU-branch binaries are published as GitHub Release attachments.

Acknowledgements & disclaimer

Built on top of the Oniro Project (Eclipse Foundation) OpenHarmony distribution images and OpenHarmony (OpenAtom Foundation). The OpenHarmony userland is not redistributed by this repository — you fetch it from the official Oniro release infrastructure yourself. Alpine Linux provides the guest base.

This project is an independent experiment. It is not affiliated with, endorsed by, or supported by the OpenAtom Foundation, the Oniro Project, Huawei, or any OpenHarmony working group. "OpenHarmony", "HarmonyOS" and related names are trademarks of their respective owners; they are used here only to refer to the upstream projects.

Licensed under Apache-2.0.

About

Run the official OpenHarmony (Oniro) emulator userland, byte-for-byte unmodified, inside a runc container on an Alpine Linux guest — stock QEMU, one 'make run'.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages