{"id":19478545,"url":"https://github.com/deomorxsy/eulab-poc","last_synced_at":"2026-05-18T19:04:38.924Z","repository":{"id":231793593,"uuid":"776049038","full_name":"deomorxsy/eulab-poc","owner":"deomorxsy","description":"minimal early userspace (+networking) POC for kernel dev with qemu and busybox","archived":false,"fork":false,"pushed_at":"2024-05-06T05:12:56.000Z","size":91,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-08T06:46:49.718Z","etag":null,"topics":["busybox","linux","qemu","qemu-kvm","ramdisk"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deomorxsy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-22T15:20:07.000Z","updated_at":"2024-05-07T03:17:29.000Z","dependencies_parsed_at":"2024-04-06T01:28:47.360Z","dependency_job_id":"cd9f69a4-c174-4c89-b58d-1643fa557b73","html_url":"https://github.com/deomorxsy/eulab-poc","commit_stats":null,"previous_names":["deomorxsy/eulab-poc"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deomorxsy%2Feulab-poc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deomorxsy%2Feulab-poc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deomorxsy%2Feulab-poc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deomorxsy%2Feulab-poc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deomorxsy","download_url":"https://codeload.github.com/deomorxsy/eulab-poc/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240709686,"owners_count":19845038,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["busybox","linux","qemu","qemu-kvm","ramdisk"],"created_at":"2024-11-10T19:50:24.458Z","updated_at":"2025-10-08T00:02:31.668Z","avatar_url":"https://github.com/deomorxsy.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eulab-poc\n\u003e simple early userspace lab proof-of-concept for linux kernel dev with qemu and busybox\n\n[![Generate ramdisk](https://github.com/deomorxsy/eulab-poc/actions/workflows/ramdisk-builder.yml/badge.svg)](https://github.com/deomorxsy/eulab-poc/actions/workflows/ramdisk-builder.yml)\n[![Generate bzImage](https://github.com/deomorxsy/eulab-poc/actions/workflows/kernel-builder.yml/badge.svg)](https://github.com/deomorxsy/eulab-poc/actions/workflows/kernel-builder.yml)\n\n## Lab setup\n\nCombine a kernel bzImage piggy and a initramfs ramdisk and boot it into QEMU. For actual boot into a machine, you will need a bootloader.\n\nUsage:\n1. To get Dockerfile and build context in separate directories, use compose.\n2. To tinker with the builds, use any OCI container runtime\n3. To check how automated build fits into this, use Makefile.\n4. Want to locally test the CI? Check [@nektos/act](https://github.com/nektos/act)\n5. Want to test the artifacts? Download them into the artifacts directory and feed them into QEMU with the [qemuit function](https://github.com/deomorxsy/eulab-poc/blob/194ade5144640d079efdbc27fe25314ea56c70dd/initramfs.sh#L145) or with ```make boot```\n6. You have built any artifact with the shellscript or make and now want to get rid of it? ```make clean```.\n\n## Prerequisites and cases\n\n1. For QEMU, get the version specific for the architecture of your current CPU.\n\n2. The build environment calling the shellscript will compile a statically compiled busybox with musl-gcc instead of glibc, which heavily depends on kernel headers suited for musl. In arch, [kernel-headers-musl](https://archlinux.org/packages/extra/x86_64/kernel-headers-musl/) is the [PKGBUILD](https://gitlab.archlinux.org/archlinux/packaging/packages/kernel-headers-musl/-/blob/main/PKGBUILD?ref_type=heads) porting the kernel headers provided by the [Sabotage linux](https://github.com/sabotage-linux/kernel-headers) distro.\n\n3. The previous statement also means that to be able to build this in a OCI container, it have to be running under a linux distro with previously installed kernel headers, be it a host OS or a guest OS virtual machine. If the compilation is not the point, but the use cases of the binary (get an initramfs ramdisk working) just use the [official image](https://hub.docker.com/_/busybox) as previous step with multi-stage containerfile build.\n\n4. The networking part is being handled by the TUN/TAP device method, using iproute2. The main idea is to run the network scripts as root but not the VM. Also, most of the QEMU documentation references debian-related networking scripts such as ifup, ifdown and ifconfig, which depends on the net-tools package that isn't covered here, and that can be replaced by iproute2.\n\n5. Finally, the VLAN is handled using iproute2. This repo explores these two ways for Bridges:\n- iptables bridge routing\n- tun/tap bridge\n\n## Booting the VM\n\nThe tricky part is the networking. As I've wrote [here](https://deomorxsy.github.io/tech/tap-virtual-networking-for-qemu/), there are 4 main points to grasp it fully:\n\n- The relation between devices and interfaces under Linux (specifically for networking)\n- The different packages to achieve this: the old net-tools and iproute2. This guide tries to use iproute2 tooling.\n- Distro-specific scripts that wraps around tooling for virtual network setups. An example is Debian’s ifup(8) and ifdown(8) which is used by LFS (Linux From Scratch) and referenced by the QEMU networking docs. These are cited by a lot of QEMU guides for bridging, also having some late versions distributing similar scripts under /var\n- Deprecated tools that were used in tutorials the last 15 years or so, like brctl that wraps around net-tools.\n\n\nFor the host OS: there are multiple ways to configure, and this repo goes with the tap network interface using the TUN/TAP module, where the packets are routed by a bridge network interface. By default QEMU uses SLIRP, but have problems with performance that are improved with tap/bridge.\n\nFor the guest OS: since busybox is used along with runit's [runsv](https://smarden.org/runit/runsv.8) in init systems like openrc on gentoo or on alpine, these usually leans towards the [ifupdown-ng](https://manpages.debian.org/testing/ifupdown-ng/interfaces.5.en.html) package for network management alongside iproute2, which is also used in debian. This means that configuration can be handled by the ```/etc/network/interfaces``` file to specify how these network interfaces are configured.\n\nIn [qemu-myifup.sh](./scripts/qemu-myifup.sh) are the commands to setup the network in the host os considering the environment described above.\n\nThe first thing is to configure the bridge:\n```sh\n# create bridge, set NIC as part of bridge,\n# assign IP with CIDR subnet, bring up the bridge\nip link add name vmbr0 type bridge\nip link set enp4s0 master vmbr0\nip addr add \"192.168.0.20/24\" dev vmbr0\nip link set dev vmbr0 up\n```\n\nIf in any moment the host connection goes down, bring it back and reassign the bridge master again:\n```sh\nip link set enp4s0 nomaster\nip link set enp4s0 master vmbr0\n```\n\nAt last, on the host OS side, the option ```-net bridge``` automatically uses the helper argument i.e. ```helper=/usr/lib/qemu/qemu-bridge-helper```, but it will raise an error if the user don't have access for its execution.\n\nPermit user to use the bridge helper so you can run rootless QEMU\n```\nchmod +s  /usr/lib/qemu/qemu-bridge-helper\n```\n\nNow for the last setup:\n\n```sh\ncat \u003c\u003c EOF \u003e ./scripts/custom.sh\nrandom_mac\n\nqemu-system-x86_64 \\\n    -M pc \\\n    -kernel ./artifacts/bzImage \\\n    -initrd ./artifacts/initramfs.cpio.gz \\\n    -enable-kvm \\\n    -m 1024 \\\n    -append 'console=ttyS0 root=/dev/sda earlyprintk net.ifnames=0' \\\n    -nographic \\\n    -no-reboot \\\n    -drive file=\"./utils/storage/eulab-hd\",format=raw \\\n    -net nic,model=virtio,macaddr=\"$macaddr\" \\\n    -net bridge,br=vmbr0\nEOF\n\nchmod +x ./scripts/custom.sh\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeomorxsy%2Feulab-poc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeomorxsy%2Feulab-poc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeomorxsy%2Feulab-poc/lists"}