buninu-linux 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/Dockerfile +23 -0
  2. package/NOTICE.md +111 -63
  3. package/README.md +280 -60
  4. package/build-image.sh +9 -5
  5. package/build-uki.sh +44 -28
  6. package/fetch-alpine.sh +43 -35
  7. package/fetch-bun.sh +14 -21
  8. package/index.js +150 -35
  9. package/initramfs/bin/buninu-linux-help +12 -0
  10. package/initramfs/bin/bunterm +64 -5
  11. package/initramfs/bin/chroot +4 -1
  12. package/initramfs/bin/ip +3 -2
  13. package/initramfs/bin/ps +3 -0
  14. package/initramfs/bin/tar +9 -0
  15. package/initramfs/init.js +79 -41
  16. package/initramfs/lib/bunterm/images.js +17 -2
  17. package/initramfs/lib/bunterm/terminal.js +25 -9
  18. package/initramfs/lib/bunterm/vt.js +91 -0
  19. package/initramfs/lib/dlopen.js +4 -0
  20. package/initramfs/lib/fbdev.js +33 -10
  21. package/initramfs/usr/share/doc/buninu-linux/bunterm.md +25 -7
  22. package/initramfs/usr/share/doc/buninu-linux/fbdev.md +1 -1
  23. package/initramfs/usr/share/doc/buninu-linux/tar.md +2 -0
  24. package/initramfs/usr/share/licenses/README.md +4 -0
  25. package/native/aarch64/lib/ld-musl-aarch64.so.1 +0 -0
  26. package/native/aarch64/lib/libc.musl-aarch64.so.1 +0 -0
  27. package/native/aarch64/lib/libgcc_s.so.1 +0 -0
  28. package/native/aarch64/lib/libstdc++.so.6 +0 -0
  29. package/package.json +2 -2
  30. package/run-qemu.sh +61 -12
  31. package/scripts/arch.sh +113 -0
  32. package/scripts/fetch.sh +5 -3
  33. package/scripts/pack-initramfs.sh +22 -4
  34. package/test/host/Dockerfile +13 -0
  35. package/test/host/boot-test.exp +70 -0
  36. package/test/host/host-test.sh +49 -0
  37. package/test/host/in-container.sh +58 -0
  38. /package/{initramfs → native/x86_64}/lib/ld-musl-x86_64.so.1 +0 -0
  39. /package/{initramfs → native/x86_64}/lib/libc.musl-x86_64.so.1 +0 -0
  40. /package/{initramfs → native/x86_64}/lib/libgcc_s.so.1 +0 -0
  41. /package/{initramfs → native/x86_64}/lib/libstdc++.so.6 +0 -0
package/README.md CHANGED
@@ -5,19 +5,22 @@
5
5
  * a prebuilt kernel from Alpine
6
6
  * a prebuilt systemd EFI stub from Alpine
7
7
  * initramfs JavaScript components:
8
- * `/init.js` and other modules
8
+ * `/init.js`, Buninu userspace, and other modules
9
9
  * initramfs Native components:
10
- * Official `bun-linux-x64-musl` and its dependencies
11
- + ld-musl-x86_64.so.1
12
- + libc.musl-x86_64.so.1 (intentional copy)
10
+ * Official `bun-linux-x64-musl` (or `bun-linux-aarch64-musl`) and its dependencies
11
+ + ld-musl-x86_64.so.1 (ld-musl-aarch64.so.1)
12
+ + libc.musl-x86_64.so.1 (libc.musl-aarch64.so.1; intentional copy)
13
13
  + libgcc_s.so.1
14
14
  + libstdc++.so.6
15
+ - Boots on real x86-64 UEFI hardware; QEMU system testing supports both x86_64
16
+ and aarch64 guests.
15
17
  - The kernel runs `bun` as PID 1
16
18
  * Everything an init would normally do — mounting filesystems, loading modules, configuring the network, reaping children
17
19
  * happens in JavaScript through `bun:ffi`
18
20
  * There is no BusyBox and no C bootstrap.
19
21
 
20
- - Souce: [github.com/jjtseng93/buninu-linux](https://github.com/jjtseng93/buninu-linux)
22
+ - Homepage: <https://buninu.org>
23
+ - Source: [github.com/jjtseng93/buninu-linux](https://github.com/jjtseng93/buninu-linux)
21
24
  - Buninu userspace source: [github.com/jjtseng93/buninu](https://github.com/jjtseng93/buninu)
22
25
  + **BUNinu Is Not Unix** 🐮
23
26
  + **幫你牛** 🐂 ・ **Bunに入魂** 🔥
@@ -30,14 +33,19 @@
30
33
  - Still in the early stages
31
34
  - [Video here](https://www.reddit.com/r/bun/comments/1wkpraj/buninu_linux_a_distro_with_bun_as_pid_1): Booted successfully on real x86-64 UEFI hardware with `--real`:
32
35
  * Bun reaches its interactive REPL
33
- * start() starts the Buninu userspace shell
36
+ * `start` starts the Buninu userspace shell
34
37
  * `cfg.disk` + shell `mount` mounts local disks
35
38
  * `cfg.net` loads common wired NIC and Android USB-tethering drivers
36
39
  * Android phone USB tethering over RNDIS has been tested successfully, allowing Buninu Linux to access the Internet through an Android phone
37
40
  * IP addresses and routes are configured manually because the image does not yet include a DHCP client.
38
- * The bundled `jmi` editor, jsmdcui editor/terminal multitasking, and local JavaScript execution also work.
41
+ * The bundled `jmi` editor, jsmdcui App runtime, and local JavaScript execution also work.
42
+ * Multi-tasking with kernel VT or jsmdcui
43
+ + See [Multitasking modes](#multitasking-modes)
39
44
  * The graphical terminal `bunterm` draws on the framebuffer with CJK, colour emoji and kitty images; see [Showing images](#showing-images)
40
45
  + Also has mouse click, wheel, and cursor drawing
46
+ + Simplified browser works: @sanohiro/casty in chroot debian
47
+ + (Use at your own risk: We haven't fully examined its code)
48
+ + See [Commands inside `/bin`](#commands-inside-bin) for browser usage instructions
41
49
 
42
50
  ---
43
51
 
@@ -46,6 +54,11 @@
46
54
 
47
55
  ## Quick start
48
56
 
57
+ > [!IMPORTANT]
58
+ > This section builds an image for real x86-64 UEFI hardware. To build and
59
+ > test either an x86_64 or aarch64 guest with `qemu-system` instead, see
60
+ > [Build and test with QEMU on macOS or Linux](#build-and-test-with-qemu-on-macos-or-linux).
61
+
49
62
  - This guide builds a bootable `buninu-linux-<version>.img`
50
63
  - The documented build environment is Debian 13 under Termux PRoot
51
64
  - A regular Debian installation works as well.
@@ -75,9 +88,11 @@ apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parte
75
88
  mkdir -p /data/data/com.termux/files/home/buninu-build
76
89
  cd /data/data/com.termux/files/home/buninu-build
77
90
 
78
- $HOME/.bun/bin/bun x buninu-linux --version
91
+ export PATH=$HOME/.bun/bin:$PATH
92
+
93
+ bun x buninu-linux --version
79
94
 
80
- $HOME/.bun/bin/bun x buninu-linux -fb --real --export
95
+ bun x buninu-linux -fb --real --export
81
96
  ```
82
97
 
83
98
  `--export` copies the completed `vda.img` out of bunx's package directory and
@@ -113,6 +128,10 @@ this overwrites it completely.
113
128
  sudo dd if='buninu-linux-<version>.img' of=/dev/sdX bs=4M conv=fsync status=progress
114
129
  ```
115
130
 
131
+ Alternatively, copy the exported `.img` file to a
132
+ [Ventoy](https://www.ventoy.net/en/index.html) USB drive and select it from
133
+ Ventoy's boot menu.
134
+
116
135
  #### Booting from the USB drive
117
136
 
118
137
  Boot that drive as x86-64 UEFI media. The UKI is unsigned, so disable Secure
@@ -146,6 +165,110 @@ described under [Basic configuration](#basic-configuration). Run `poweroff` for
146
165
  shutdown. After editing the initramfs, `bun ./index.js -b --real --export` rebuilds the
147
166
  image; `-h` lists all flags.
148
167
 
168
+ ### Build and test with QEMU on macOS or Linux
169
+
170
+ > [!IMPORTANT]
171
+ > This section covers guests built and run with `qemu-system` only. To build
172
+ > an image for real x86-64 UEFI hardware, follow [Quick start](#quick-start)
173
+ > above and use `-fb --real --export`.
174
+
175
+ The same `index.js` builds and boots Buninu Linux in QEMU on a Mac or Linux
176
+ host. `--arch` picks the guest:
177
+
178
+ | host | `--arch x86_64` (default) | `--arch aarch64` (alias `arm64`) |
179
+ | --- | --- | --- |
180
+ | Apple Silicon Mac | q35, emulated (TCG) | `virt`, hardware-accelerated (Hypervisor.framework) |
181
+ | Intel Mac | q35, hardware-accelerated (Hypervisor.framework) | `virt`, emulated (TCG) |
182
+ | x86-64 Linux | q35, hardware-accelerated (KVM) when `/dev/kvm` is usable | `virt`, emulated (TCG) |
183
+ | arm64 Linux | q35, emulated (TCG) | `virt`, hardware-accelerated (KVM) when `/dev/kvm` is usable |
184
+ | Android Termux | q35, emulated (TCG) | `virt`, KVM when `/dev/kvm` is usable, otherwise TCG |
185
+
186
+ `run-qemu.sh` chooses the accelerator itself; `ACCEL=tcg` forces emulation.
187
+
188
+ #### macOS
189
+
190
+ This project does not currently support a native macOS build toolchain (GNU
191
+ cpio and tar, `fakeroot`, `parted`, PE-aware binutils), so on a Mac `index.js`
192
+ always runs the fetch and build stages in a Debian 13 container built from
193
+ [`Dockerfile`](Dockerfile), with the checkout bind-mounted. The image is built
194
+ on first use and tagged with a hash of the Dockerfile. Only the run stage uses
195
+ the Mac itself:
196
+
197
+ ```sh
198
+ # Docker Desktop (running) for -f/-b, QEMU and its UEFI firmware for -r
199
+ brew install qemu
200
+
201
+ # Apple Silicon: Hypervisor.framework accelerates the aarch64 guest
202
+ bun ./index.js -fbr --arch arm64
203
+
204
+ # The x86_64 guest works too, emulated
205
+ bun ./index.js -fbr
206
+ ```
207
+
208
+ The first boot line from `run-qemu.sh` names the guest, machine, accelerator
209
+ and firmware it picked. A bare `-r` boots whichever architecture `vda.img`
210
+ was last built for; `-br --arch arm64` is the aarch64 edit-and-boot loop.
211
+
212
+ #### amd64 Linux
213
+
214
+ On Debian or Ubuntu the build runs on the host exactly as in PRoot, and QEMU
215
+ uses KVM for the x86_64 guest when the user can open `/dev/kvm`:
216
+
217
+ ```sh
218
+ apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
219
+ apt install qemu-system-x86 ovmf
220
+ bun ./index.js -fbr
221
+ ```
222
+
223
+ Pass `--docker` to build in the same container as macOS instead of installing
224
+ the build packages; the aarch64 guest additionally needs
225
+ `binutils-aarch64-linux-gnu` for a host build and `qemu-system-arm
226
+ qemu-efi-aarch64` to run.
227
+
228
+ #### What differs on aarch64
229
+
230
+ * The image is `vda/EFI/BOOT/BOOTAA64.EFI` on the same GPT disk, built from
231
+ Alpine's aarch64 packages and `bun-linux-aarch64-musl`.
232
+ * The serial console is `ttyAMA0` (QEMU `virt`'s PL011) instead of `ttyS0`.
233
+ * `--real` is x86_64 only: its module set is PC hardware.
234
+ * The current aarch64 QEMU configuration supplies no framebuffer or PS/2
235
+ devices, so `bunterm` has nothing to draw on yet; the serial REPL and the
236
+ Buninu shell work as on x86_64.
237
+ * `--export` writes `buninu-linux-<version>-aarch64.img`.
238
+ * Before the kernel starts, Homebrew's aarch64 EDK2 prints a few
239
+ `Error: Image at … start failed` lines (its own drivers for hardware `virt`
240
+ does not have) and `ConvertPages: failed to find range …` (it cannot place
241
+ the UKI at the stub's preferred address and relocates it). Both are
242
+ harmless; `BdsDxe: starting Boot0001` follows and the boot continues.
243
+
244
+ #### Testing on a Linux host in Docker
245
+
246
+ `test/host/host-test.sh` checks the Linux-host path from any machine with
247
+ Docker: it starts a fresh Debian 13 container of the chosen CPU with the
248
+ build packages above and QEMU, clones the committed `HEAD` of this checkout,
249
+ builds both guests with that toolchain (not `--docker`) and boots each one,
250
+ driving the serial console through `test/host/boot-test.exp`:
251
+
252
+ ```sh
253
+ test/host/host-test.sh arm64 # a Linux arm64 host (native on Apple Silicon)
254
+ test/host/host-test.sh amd64 # a Linux x86-64 host (emulated there)
255
+ ```
256
+
257
+ Every step — REPL, `start()`, `uname`, eth0, PID 1, a tmpfs mount, a fetch
258
+ from the guest, `poweroff` — waits for its expected output, and the run ends
259
+ with `### PASS` and exit status 0 or `### FAIL` and 1. Uncommitted changes to
260
+ the project are not tested; commit first. `GUESTS=aarch64` limits the guests,
261
+ `BUILD_FLAGS=--linux-lts` adds build flags, and `downloads/` is used read-only
262
+ as the download cache. Docker Desktop exposes no `/dev/kvm`, so there both
263
+ guests run under TCG; on a Linux host with KVM the script passes it through
264
+ and the matching guest boots with `accel=kvm`. On macOS the same
265
+ `boot-test.exp` also drives a local boot: `expect test/host/boot-test.exp
266
+ "$PWD" aarch64` after `bun ./index.js -b --arch arm64`.
267
+
268
+ `ALPINE_MIRROR` swaps the Alpine CDN for a mirror, e.g.
269
+ `ALPINE_MIRROR=https://mirrors.edge.kernel.org/alpine`; every download is still
270
+ checked against its pinned SHA-256.
271
+
149
272
  ## Using Buninu Linux
150
273
 
151
274
  ### Basic configuration
@@ -213,7 +336,36 @@ bun hlw.js
213
336
  If `Enter` is not recognized in a particular terminal, try `Ctrl-J` or
214
337
  `Ctrl-M`.
215
338
 
216
- ### Panes, terminals, and tabs
339
+ #### jsmdcui
340
+ - `jsmdcui` is the same editor, but by default executes markdown files as interactive Apps
341
+ - Be careful not to run markdown files from strangers
342
+ * Otherwise it can run any command on your PC
343
+ - `jsmdcui --cdp-maze` runs a self-solving maze game
344
+ * Run `bunterm` first to show its Emojis
345
+
346
+ ### Multitasking modes
347
+
348
+ #### Multiple virtual consoles with bunterm
349
+
350
+ Run a separate `bunterm` on each Linux virtual console. A numeric positional
351
+ argument is a short form of the corresponding device, so `bunterm 2` means
352
+ `bunterm /dev/tty2`:
353
+
354
+ ```sh
355
+ bunterm 2
356
+ bunterm 3 -s 13
357
+ ```
358
+
359
+ When the named VT is not the active one, `bunterm` starts there as a detached
360
+ session, switches the display to it, and immediately returns control to the
361
+ calling shell. Switch among VTs with `Ctrl+Alt+F1` through `Ctrl+Alt+F12`, or
362
+ move to the previous or next VT with `Alt+Left` and `Alt+Right`. Each VT keeps
363
+ its own terminal session; an inactive bunterm continues processing its PTY but
364
+ stops drawing until its VT becomes active again. Only one bunterm can control a
365
+ given VT. If one is already in use, the error identifies its PID and suggests
366
+ another VT number.
367
+
368
+ #### Panes, terminals, and tabs with jsmdcui
217
369
 
218
370
  `jmi` and jsmdcui can keep editors and terminal sessions open together. Press
219
371
  `Ctrl-E`, type `term`, and press `Enter` to open the default Buninu shell in a
@@ -222,7 +374,7 @@ terminal pane. `term COMMAND` starts a specific command instead. Use `vsplit`,
222
374
  editor pane or tab; each command optionally accepts a filename. From an editor
223
375
  pane, `Ctrl-T` is the direct shortcut for a new empty tab.
224
376
 
225
- #### Pane and tab controls
377
+ ##### Pane and tab controls
226
378
 
227
379
  | Input | Result |
228
380
  | --- | --- |
@@ -329,6 +481,7 @@ needs a kernel with a framebuffer: build with `--linux-lts` or `--real`
329
481
 
330
482
  | command | does | manual |
331
483
  | --- | --- | --- |
484
+ | `buninu-linux-help` | render this buninu-linux README from `/usr/share/doc/buninu-linux/README.md` | `buninu-linux-help` |
332
485
  | `mount` | `mount(2)` with type detection, `-o` parsing, `LABEL=`/`UUID=`, bind/move/remount; loads required filesystem and disk modules | `mount --help` → `/usr/share/doc/buninu-linux/mount.md` |
333
486
  | `umount` | `umount2(2)` with `-l`, `-f`, `-R` | `umount --help` |
334
487
  | `ip` | iproute2 grammar over `SIOC*` ioctls and `/proc/net`: `link`, `addr`, `route`, `neigh` | `ip --help` |
@@ -341,7 +494,7 @@ needs a kernel with a framebuffer: build with `--linux-lts` or `--real`
341
494
  | `chroot` | `chroot(2)` into another root directory, mounting `/proc`, `/sys`, `/dev`, `/dev/pts`, `/run`, `/tmp` and `/etc/resolv.conf` for you and unmounting them afterwards | `chroot --help` |
342
495
  | `bunterm` | a graphical terminal on the framebuffer: Skia (CanvasKit) text with CJK, colour emoji, seamless box drawing, kitty graphics images, with the mouse working, wheel scrollback included (`--no-mouse` turns it off); xterm.js's emulator core over Bun's built-in PTY | `bunterm --help` |
343
496
 
344
- Besides `bun` (and `sh`/`node` pointing at it), `/bin` includes eleven commands
497
+ Besides `bun` (and `sh`/`node` pointing at it), `/bin` includes twelve commands
345
498
  implemented as Bun scripts.
346
499
 
347
500
  The small `tar` reads the archive's own headers for links and listing, so
@@ -352,9 +505,11 @@ only regular files. Creation still follows the `Bun.Archive` boundary: it
352
505
  stores regular files but not Unix metadata, links, or empty directories. It
353
506
  supports gzip and Bun's built-in zstd, not xz or bzip2.
354
507
 
355
- `--help` renders each Markdown manual in the terminal. The `bun x` examples
356
- below require network access: `bun x` downloads the requested program from the
357
- npm registry and runs it without installing it permanently. Common examples:
508
+ - `--help` renders each Markdown manual in the terminal.
509
+ - The `bun x` examples below require network access:
510
+ * `bun x` downloads the requested program from the npm registry and runs it without installing it permanently.
511
+ * If they fail the 1st time, try `bun pm cache rm`
512
+ - Common examples:
358
513
 
359
514
  ```sh
360
515
  mount /dev/sda1 /mnt --mkdir
@@ -374,6 +529,41 @@ bun x bunproot --git --readme | stripansi | jmi
374
529
 
375
530
 
376
531
 
532
+ # Download and enter an x64 Debian rootfs
533
+ # Make sure you have run this already:
534
+ # bunterm --font-size 13
535
+ bun x bunproot --git --yes clone https://github.com/jjtseng93/js-udocker
536
+ cd js-udocker
537
+ bun udocker.js pull --platform=linux/amd64 debian:13
538
+ bun udocker.js create --name db debian:13
539
+ cd ~/.udocker/containers/db/ROOT
540
+ chroot . /bin/bash
541
+ apt update
542
+ apt install --no-install-recommends ca-certificates curl unzip chromium-headless-shell fonts-noto-cjk fonts-noto-color-emoji git
543
+ curl -fsSL https://bun.sh/install | bash
544
+ bun x bunmsh
545
+ # We haven't fully reviewed
546
+ # @sanohiro/casty's safety
547
+ # below is a fork of mine
548
+ # fixing some issues
549
+ # !!!Use at your own risk!!!
550
+ # !!!Use at your own risk!!!
551
+ # !!!Use at your own risk!!!
552
+ # !!!Use at your own risk!!!
553
+ # !!!Use at your own risk!!!
554
+ # !!!Use at your own risk!!!
555
+ # !!!Use at your own risk!!!
556
+ # !!!Use at your own risk!!!
557
+ # !!!Use at your own risk!!!
558
+ # !!!Use at your own risk!!!
559
+ cd
560
+ git clone https://github.com/jjtseng93/casty
561
+ cd casty/bin
562
+ # When clicking around, don’t release the mouse button immediately after pressing it, to make sure the mouse-down event is triggered
563
+ bun casty.js buninu.org
564
+
565
+
566
+
377
567
  # Download and enter an x64 Alpine minirootfs
378
568
  bun x bunproot --download-alpine-x64
379
569
  mkdir alpine
@@ -420,7 +610,7 @@ tar czvf /tmp/buninu.tar.gz README.md package.json
420
610
 
421
611
  # On a virtual console: a graphical terminal with CJK, emoji and images
422
612
  bunterm
423
- bunterm /dev/tty1 --font-size 20 -e bun /buninu/apps/jsmdcui/src/index.js --demo
613
+ bunterm /dev/tty1 --font-size 13 -e bun /buninu/apps/jsmdcui/src/index.js --demo
424
614
  ```
425
615
 
426
616
  The graphics stack behind `bunterm` — the framebuffer module, CanvasKit,
@@ -536,10 +726,13 @@ it too). Its physical-image stages run as fetch → build:
536
726
  | --- | --- | --- |
537
727
  | `-f`, `--fetch` | `fetch-alpine.sh`, `fetch-bun.sh` | first clone, or after bumping a pinned version |
538
728
  | `-b`, `--build` | `build-uki.sh`, `build-image.sh` | after editing `initramfs/init.js` or the kernel command line |
729
+ | `--arch ARCH` | explicitly selects `x86_64` (default for fetch/build) or `aarch64`; a bare `-r` detects the last built UKI | building or running a particular guest architecture |
730
+ | `--docker` | `-f`/`-b` inside the container from `Dockerfile` (always on macOS) | no Debian toolchain on the host |
539
731
 
540
732
  `--export` is a post-build option: after `-b` finishes, it copies `vda.img` to
541
733
  the directory where the command was invoked as
542
- `buninu-linux-<version>.img`. It therefore requires `-b`/`--build` and is the
734
+ `buninu-linux-<version>.img` (`buninu-linux-<version>-aarch64.img` for
735
+ `--arch aarch64`). It therefore requires `-b`/`--build` and is the
543
736
  recommended way to retain an image produced through npx/bunx:
544
737
 
545
738
  ```sh
@@ -558,26 +751,33 @@ failing halfway. The shell scripts do the actual work and each still runs on
558
751
  its own.
559
752
 
560
753
  ```text
561
- index.js entry point: -f / -b / --export / --linux-lts / --real
754
+ index.js entry point: -f / -b / -r / --arch / --docker / --export / --linux-lts / --real
755
+ Dockerfile the Debian 13 build toolchain for --docker (and macOS)
562
756
  fetch-alpine.sh [fetch] kernel, EFI stub, musl, network/input modules
563
757
  fetch-bun.sh [fetch] Bun, libstdc++, libgcc
564
758
  build-uki.sh [build] UKI; calls scripts/pack-initramfs.sh
565
759
  build-image.sh [build] GPT disk image; shared with the hello-world EFI
760
+ run-qemu.sh [run] QEMU q35 (x86_64) or virt (aarch64), KVM/HVF/TCG
761
+ scripts/arch.sh per-architecture pins, hashes and names, sourced by all of the above
566
762
  scripts/fetch.sh hash-checked download helper, sourced by fetch-*.sh
567
763
  scripts/pack-initramfs.sh cpio archive with the device nodes
568
- hello/ hello-world EFI: hello.c and build-hello.sh
569
- test/ the retired C bootstrap and its build script
570
- initramfs/ init.js and the committed libraries; rest fetched
764
+ hello/ hello-world EFI: hello.c and build-hello.sh (x86_64 only)
765
+ test/ the retired C bootstrap and its build script (x86_64 only)
766
+ test/host/ build-and-boot test on a fresh Linux amd64/arm64 host, in Docker
767
+ initramfs/ init.js, commands, userspace: the same for every architecture
768
+ native/<arch>/ musl and the GCC runtime (committed); bun and modules (fetched)
571
769
  ```
572
770
 
573
- The `--real` inputs are Alpine v3.24 `linux-lts-6.18.52-r0`, musl `1.2.6-r2`,
771
+ The `--real` inputs are Alpine v3.24 `linux-lts-6.18.53-r0`, musl `1.2.6-r2`,
574
772
  `systemd-efistub-260.2-r0`, `libstdc++`/`libgcc` `15.2.0-r5`, and Bun 1.4.2
575
- `linux-x64-musl-baseline`. There is no BusyBox and no userland beyond Bun
576
- itself.
773
+ `linux-x64-musl-baseline`. The aarch64 guest uses the same versions from
774
+ Alpine's aarch64 repository and Bun's `linux-aarch64-musl`. There is no BusyBox
775
+ or conventional native userland; Bun and the JavaScript-based Buninu userspace
776
+ provide the commands.
577
777
 
578
778
  Every step is idempotent and `-f` re-downloads nothing: `scripts/fetch.sh`
579
- treats each pinned SHA-256 as the cache key, so a file already in `downloads/`
580
- with the right hash is used as-is, and anything missing, truncated, stale or
779
+ treats each pinned SHA-256 as the cache key, so a file already in
780
+ `downloads/<arch>/` with the right hash is used as-is, and anything missing, truncated, stale or
581
781
  tampered with is fetched again and has to pass the same hash before a script
582
782
  extracts from it. Bumping a version changes both the file name and the hash, so
583
783
  it always refetches.
@@ -592,11 +792,11 @@ and copies the final `vda.img` to the invocation directory.
592
792
 
593
793
  | script | reads | writes |
594
794
  | --- | --- | --- |
595
- | `fetch-alpine.sh` | Alpine CDN | `kernel/vmlinuz-lts`, `kernel/linuxx64.efi.stub`, musl, and the selected network, storage, input, power and filesystem modules with trimmed module indexes |
596
- | `fetch-bun.sh` | GitHub, Alpine CDN | `initramfs/bin/bun`, `initramfs/lib/{libc.musl-x86_64.so.1,libstdc++.so.6,libgcc_s.so.1}` |
597
- | `scripts/pack-initramfs.sh` | `initramfs/` | `build/initramfs.cpio.gz` |
598
- | `build-uki.sh` | that archive, kernel, stub | `build/cmdline`, `build/os-release`, `vda/EFI/BOOT/BOOTX64.EFI` |
599
- | `build-image.sh` | `vda/EFI/BOOT/BOOTX64.EFI` | `vda.img` |
795
+ | `fetch-alpine.sh` | Alpine CDN | `kernel/<arch>/vmlinuz-<flavor>`, `kernel/<arch>/linuxx64.efi.stub` (`linuxaa64.efi.stub`), `native/<arch>/lib/ld-musl-<arch>.so.1`, and the selected network, storage, input, power and filesystem modules under `native/<arch>/lib/modules/` with trimmed module indexes |
796
+ | `fetch-bun.sh` | GitHub, Alpine CDN | `native/<arch>/bin/bun`, `native/<arch>/lib/{libc.musl-<arch>.so.1,libstdc++.so.6,libgcc_s.so.1}` |
797
+ | `scripts/pack-initramfs.sh` | `initramfs/`, then `native/<arch>/` over it | `build/initramfs.cpio.gz` |
798
+ | `build-uki.sh` | that archive, kernel, stub | `build/cmdline`, `build/os-release`, `vda/EFI/BOOT/BOOTX64.EFI` (`BOOTAA64.EFI`) |
799
+ | `build-image.sh` | `vda/EFI/BOOT/BOOTX64.EFI` (`BOOTAA64.EFI`) | `vda.img` |
600
800
 
601
801
  `build-uki.sh` calls `scripts/pack-initramfs.sh` itself, so that one is rarely
602
802
  run directly.
@@ -614,8 +814,11 @@ boot.
614
814
 
615
815
  #### UKI layout
616
816
 
617
- `build-uki.sh` appends sections to systemd's `linuxx64.efi.stub` with
618
- `objcopy`, at hand-picked VMAs because `objcopy` will not lay them out for you:
817
+ `build-uki.sh` appends sections to systemd's architecture-specific EFI stub
818
+ with `objcopy`, at calculated VMAs because `objcopy` will not lay them out for
819
+ you. The following sizes and offsets describe the current x86_64 `--real`
820
+ image; aarch64 uses `linuxaa64.efi.stub`, whose larger stub moves `.osrel` and
821
+ `.cmdline`, and every build measures the inputs to prevent overlap:
619
822
 
620
823
  ##### UKI sections
621
824
 
@@ -624,11 +827,12 @@ boot.
624
827
  | `.text` | +0x0 | the stub itself | 66 KB |
625
828
  | `.osrel` | +0x20000 | `build/os-release` | 78 B |
626
829
  | `.cmdline` | +0x30000 | `build/cmdline` | 136 B |
627
- | `.linux` | +0x2000000 | `kernel/vmlinuz-lts` | 14.5 MB |
830
+ | `.linux` | +0x2000000 | `kernel/x86_64/vmlinuz-lts` | 14.5 MB |
628
831
  | `.initrd` | +0x3000000 | `build/initramfs.cpio.gz` | 44.6 MB |
629
832
 
630
- The current `--real` result is a single 59.3 MB PE32+ file holding kernel, initramfs and command
631
- line.
833
+ The current x86_64 `--real` result is a single 59.3 MB PE32+ file holding the
834
+ kernel, initramfs and command line. Other architectures, flavors and source
835
+ revisions can produce different sizes.
632
836
 
633
837
  #### Disk layout
634
838
 
@@ -643,13 +847,15 @@ rejects the volume.
643
847
  LBA 0 protective MBR
644
848
  LBA 1..33 primary GPT
645
849
  LBA 2048..262110 ESP, FAT32 "EFIBOOT", type GUID C12A7328-F81F-11D2-BA4B-00A0C93EC93B
646
- /EFI/BOOT/BOOTX64.EFI the UKI
850
+ /EFI/BOOT/BOOTX64.EFI x86_64 UKI
851
+ /EFI/BOOT/BOOTAA64.EFI aarch64 UKI (in an aarch64 image)
647
852
  LBA 262111.. backup GPT
648
853
  ```
649
854
 
650
855
  128 MiB sparse, 1 MiB-aligned, with 34 sectors reserved at the end for the
651
- backup GPT. `EFI/BOOT/BOOTX64.EFI` is the removable-media fallback path, so the
652
- firmware runs it without any NVRAM boot entry.
856
+ backup GPT. `EFI/BOOT/BOOTX64.EFI` and `EFI/BOOT/BOOTAA64.EFI` are the
857
+ architecture-specific removable-media fallback paths, so matching firmware
858
+ runs the one present in the image without any NVRAM boot entry.
653
859
 
654
860
  #### Real-hardware boot details
655
861
 
@@ -659,7 +865,7 @@ The physical boot chain is firmware → the UKI stub → its embedded
659
865
  and, with the currently pinned kernel, embeds this complete command line:
660
866
 
661
867
  ```text
662
- console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-lts rdinit=/bin/bun -- -e import('/init.js')
868
+ console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.53-0-lts rdinit=/bin/bun -- -e import('/init.js')
663
869
  ```
664
870
 
665
871
  Serial kernel logging is retained, while the final console makes
@@ -676,12 +882,14 @@ runner separately from the PRoot build dependencies:
676
882
  pkg install qemu-system-x86-64
677
883
  ```
678
884
 
679
- That package supplies OVMF at `$PREFIX/share/qemu/edk2-x86_64-code.fd`. Build
680
- without `--real`; this selects Alpine `linux-virt`, omits the physical-hardware
885
+ That package supplies OVMF at `$PREFIX/share/qemu/edk2-x86_64-code.fd`, next to
886
+ QEMU's own binary, which is where `run-qemu.sh` looks after `OVMF_FILE` and
887
+ Debian's `/usr/share/OVMF/OVMF_CODE_4M.fd` (Homebrew's `share/qemu` is found
888
+ the same way). Build without `--real`; this selects Alpine `linux-virt`, omits the physical-hardware
681
889
  module set, and puts `ttyS0` last so serial stdio owns `/dev/console`:
682
890
 
683
891
  ```text
684
- console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-virt rdinit=/bin/bun -- -e import('/init.js')
892
+ console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.53-0-virt rdinit=/bin/bun -- -e import('/init.js')
685
893
  ```
686
894
 
687
895
  Build and boot it with:
@@ -690,8 +898,7 @@ Build and boot it with:
690
898
  bun ./index.js -fb
691
899
 
692
900
  # When running inside PRoot with Termux's native qemu-system-x86_64,
693
- # expose both the native executable and its OVMF path:
694
- PREFIX=/data/data/com.termux/files/usr \
901
+ # put it on PATH; its OVMF is found next to it:
695
902
  PATH="/data/data/com.termux/files/usr/bin:$PATH" \
696
903
  bun ./index.js -r
697
904
  ```
@@ -699,9 +906,11 @@ bun ./index.js -r
699
906
  If QEMU is installed inside the current Debian environment instead, plain
700
907
  `bun ./index.js -r` is sufficient; `run-qemu.sh` will use Debian's OVMF path.
701
908
 
702
- `bun ./index.js -r` runs `run-qemu.sh`, which boots `vda.img` on q35 under TCG
703
- with 512 MiB, a virtio disk, virtio-net user networking, no display, and COM1
704
- on stdio. Anything after `--` is appended to the QEMU command line. Ctrl-C
909
+ `bun ./index.js -r` runs `run-qemu.sh`, which boots `vda.img` on q35 (the
910
+ aarch64 image on `virt`) with 1 GiB, a virtio disk, virtio-net user
911
+ networking, no display, and the first serial port on stdio. It uses KVM or
912
+ Hypervisor.framework when the guest matches the host CPU and TCG otherwise;
913
+ see [Build and test with QEMU on macOS or Linux](#build-and-test-with-qemu-on-macos-or-linux). Anything after `--` is appended to the QEMU command line. Ctrl-C
705
914
  quits QEMU. Leaving the REPL does not end the session: `init.js` restarts it,
706
915
  because a PID 1 that exits panics the kernel.
707
916
 
@@ -717,7 +926,7 @@ For an even shorter initramfs loop, bypass the UKI and disk image:
717
926
 
718
927
  ```sh
719
928
  qemu-system-x86_64 -machine q35,accel=tcg -cpu max -m 512M \
720
- -kernel kernel/vmlinuz-virt -initrd build/initramfs.cpio.gz \
929
+ -kernel kernel/x86_64/vmlinuz-virt -initrd build/initramfs.cpio.gz \
721
930
  -append "console=ttyS0,115200 panic=0 PATH=/bin rdinit=/bin/bun -- -e console.log(1+1)" \
722
931
  -nic user,model=virtio-net-pci -display none -serial stdio -no-reboot
723
932
  ```
@@ -771,10 +980,11 @@ modules in `/lib`. `--help` uses `Bun.markdown.ansi` to render the Markdown
771
980
  manual with hyperlinks, and prints the page's absolute path at the end so it
772
981
  can be read or edited directly. The shared pieces are:
773
982
 
774
- * `/lib/dlopen.js` — one `bun:ffi` binding to `/lib/libc.musl-x86_64.so.1`
983
+ * `/lib/dlopen.js` — one `bun:ffi` binding to `/lib/libc.musl-<arch>.so.1`
775
984
  (`mount`, `umount2`, `ioctl`, `socket`, `sync`, `reboot`, `syscall`, …), `errno`/`strerror`,
776
985
  a `SysError` class and the `showDocument()` helper behind every `--help`.
777
- `init.js` keeps its own copy of the binding because it runs before `/proc`
986
+ `process.arch` picks the libc file name and the `finit_module` syscall
987
+ number, the only raw syscall the commands make. `init.js` keeps its own copy of the binding because it runs before `/proc`
778
988
  exists and cannot import anything.
779
989
  * `/lib/modprobe.js` — `modprobe(name)` resolves `modules.alias` and
780
990
  `modules.dep` under `/lib/modules/<release>/` and calls `finit_module`
@@ -796,7 +1006,7 @@ The default `--real` image embeds this kernel command line (shown for the
796
1006
  currently pinned LTS release):
797
1007
 
798
1008
  ```text
799
- console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-lts rdinit=/bin/bun -- -e import('/init.js')
1009
+ console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.53-0-lts rdinit=/bin/bun -- -e import('/init.js')
800
1010
  ```
801
1011
 
802
1012
  The physical display (`tty0`) is the final console and therefore owns PID 1's
@@ -863,8 +1073,8 @@ and the available `cfg` getters again. The loader and libc paths
863
1073
  contain identical bytes but are deliberately distinct inodes:
864
1074
 
865
1075
  ```text
866
- /lib/ld-musl-x86_64.so.1
867
- /lib/libc.musl-x86_64.so.1
1076
+ /lib/ld-musl-x86_64.so.1 /lib/ld-musl-aarch64.so.1
1077
+ /lib/libc.musl-x86_64.so.1 /lib/libc.musl-aarch64.so.1
868
1078
  ```
869
1079
 
870
1080
  Calling `dlopen` on the same musl inode that is already acting as Bun's loader
@@ -911,11 +1121,19 @@ for the userspace session.
911
1121
  + [Or build from a source checkout](#or-build-from-a-source-checkout)
912
1122
  + [Write the image to a USB drive](#write-the-image-to-a-usb-drive)
913
1123
  + [Booting from the USB drive](#booting-from-the-usb-drive)
1124
+ * [Build and test with QEMU on macOS or Linux](#build-and-test-with-qemu-on-macos-or-linux)
1125
+ + [macOS](#macos)
1126
+ + [amd64 Linux](#amd64-linux)
1127
+ + [What differs on aarch64](#what-differs-on-aarch64)
1128
+ + [Testing on a Linux host in Docker](#testing-on-a-linux-host-in-docker)
914
1129
  - [Using Buninu Linux](#using-buninu-linux)
915
1130
  * [Basic configuration](#basic-configuration)
916
1131
  * [Editor quick start](#editor-quick-start)
917
- * [Panes, terminals, and tabs](#panes-terminals-and-tabs)
918
- + [Pane and tab controls](#pane-and-tab-controls)
1132
+ + [jsmdcui](#jsmdcui)
1133
+ * [Multitasking modes](#multitasking-modes)
1134
+ + [Multiple virtual consoles with bunterm](#multiple-virtual-consoles-with-bunterm)
1135
+ + [Panes, terminals, and tabs with jsmdcui](#panes-terminals-and-tabs-with-jsmdcui)
1136
+ - [Pane and tab controls](#pane-and-tab-controls)
919
1137
  * [Using Bun Modern Shell](#using-bun-modern-shell)
920
1138
  * [Showing images](#showing-images)
921
1139
  - [Commands inside /bin](#commands-inside-bin)
@@ -959,15 +1177,16 @@ MIT License; see [LICENSE](LICENSE). The Buninu userspace under
959
1177
  `initramfs/buninu/` is also MIT (`initramfs/buninu/LICENSE`) and carries its
960
1178
  own third-party notices, listed at the end of [NOTICE.md](NOTICE.md).
961
1179
 
962
- The repository also commits four third-party platform binaries under
963
- `initramfs/lib/`, and the built image redistributes several more. Each stays
1180
+ The repository also commits four third-party platform binaries per guest
1181
+ architecture under `native/<arch>/lib/`, and the built image redistributes
1182
+ several more. Each stays
964
1183
  under its own terms, with the full texts in [`LICENSES/`](LICENSES/):
965
1184
 
966
1185
  ### Bundled component licenses
967
1186
 
968
1187
  | Component | License | Where |
969
1188
  |---|---|---|
970
- | musl (`ld-musl-x86_64.so.1`, `libc.musl-x86_64.so.1`) | MIT | committed |
1189
+ | musl (`ld-musl-x86_64.so.1`, `libc.musl-x86_64.so.1`, and the `aarch64` pair) | MIT | committed |
971
1190
  | GCC runtime (`libgcc_s.so.1`, `libstdc++.so.6`) | GPL-3.0-or-later with the GCC Runtime Library Exception | committed |
972
1191
  | Linux kernels and networking, storage, xHCI, USB and HID modules | GPL-2.0-only with the Linux syscall note | image only |
973
1192
  | systemd EFI stub | LGPL-2.1-or-later | image only |
@@ -986,4 +1205,5 @@ lets them ship next to MIT-licensed code, so nothing here changes Buninu
986
1205
  Linux's own terms — but if your organisation screens for GPL, these are the
987
1206
  components it will find. [NOTICE.md](NOTICE.md) records the
988
1207
  exact Alpine build of every component, the pinned aports commit that is its
989
- Corresponding Source, and SHA-256 sums for every binary.
1208
+ Corresponding Source, package SHA-256 sums and selected extracted-file
1209
+ SHA-256 sums.
package/build-image.sh CHANGED
@@ -3,9 +3,13 @@ set -euo pipefail
3
3
 
4
4
  cd "$(dirname "$0")"
5
5
 
6
+ . ./scripts/arch.sh
7
+
6
8
  mkdir -p vda/EFI/BOOT
7
- test -s vda/EFI/BOOT/BOOTX64.EFI || {
8
- echo "error: build stage: no EFI payload yet; run ./build-uki.sh (./index.js -b) or ./hello/build-hello.sh first" >&2
9
+ test -s "vda/EFI/BOOT/$efi_boot_name" || {
10
+ hint="./build-uki.sh (./index.js -b)"
11
+ [ "$arch" = x86_64 ] && hint="$hint or ./hello/build-hello.sh"
12
+ echo "error: build stage: no $arch EFI payload vda/EFI/BOOT/$efi_boot_name yet; run $hint first" >&2
9
13
  exit 1
10
14
  }
11
15
 
@@ -13,7 +17,7 @@ test -s vda/EFI/BOOT/BOOTX64.EFI || {
13
17
  # it into a GPT disk. No mount, loop device, or root privilege is required.
14
18
  for required_tool in parted mkfs.fat mmd mcopy; do
15
19
  command -v "$required_tool" >/dev/null || {
16
- echo "error: build stage needs $required_tool; run inside PRoot, see README section 0" >&2
20
+ echo "error: build stage needs $required_tool; run inside PRoot or with ./index.js --docker, see README Build environment" >&2
17
21
  exit 1
18
22
  }
19
23
  done
@@ -29,7 +33,7 @@ esp_sectors=$((esp_end - esp_start + 1))
29
33
  truncate -s $((esp_sectors * 512)) "$temporary_esp"
30
34
  mkfs.fat -F 32 -h "$esp_start" -n EFIBOOT "$temporary_esp" >/dev/null
31
35
  mmd -i "$temporary_esp" ::/EFI ::/EFI/BOOT
32
- mcopy -i "$temporary_esp" vda/EFI/BOOT/BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
36
+ mcopy -i "$temporary_esp" "vda/EFI/BOOT/$efi_boot_name" "::/EFI/BOOT/$efi_boot_name"
33
37
 
34
38
  rm -f vda.img
35
39
  truncate -s $((disk_sectors * 512)) vda.img
@@ -41,4 +45,4 @@ dd if="$temporary_esp" of=vda.img bs=512 \
41
45
  seek="$esp_start" count="$esp_sectors" \
42
46
  conv=notrunc,sparse status=none
43
47
 
44
- echo "Packed vda/EFI/BOOT/BOOTX64.EFI into vda.img"
48
+ echo "Packed vda/EFI/BOOT/$efi_boot_name into vda.img"