Skip to content

Commit

Permalink
support custom initramfs, build a non-debug kernel by default
Browse files Browse the repository at this point in the history
`make defconfig debug.config` will bring back the old options
  • Loading branch information
tombl committed Jan 5, 2025
1 parent 4c45944 commit 715da7d
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 64 deletions.
24 changes: 0 additions & 24 deletions arch/wasm/configs/defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -52,30 +52,6 @@ CONFIG_EXT2_FS=y
# CONFIG_INOTIFY_USER is not set
# CONFIG_PROC_FS is not set
# CONFIG_SYSFS is not set
CONFIG_SQUASHFS=y
CONFIG_PRINTK_TIME=y
CONFIG_PRINTK_CALLER=y
# CONFIG_DEBUG_MISC is not set
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_FRAME_WARN=2048
CONFIG_DEBUG_FS=y
CONFIG_UBSAN=y
CONFIG_PAGE_EXTENSION=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
CONFIG_DEBUG_OBJECTS_WORK=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_DEBUG_VM=y
CONFIG_DEBUG_VM_RB=y
CONFIG_DEBUG_VM_PGFLAGS=y
CONFIG_DEBUG_PER_CPU_MAPS=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_SOFTLOCKUP_DETECTOR=y
CONFIG_DEBUG_IRQFLAGS=y
CONFIG_BUG_ON_DATA_CORRUPTION=y
# CONFIG_RCU_TRACE is not set
# CONFIG_RUNTIME_TESTING_MENU is not set
36 changes: 16 additions & 20 deletions tools/wasm/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,25 +70,10 @@
</p>

<p>
It's only early days for this port, and it doesn't make it all the way
through boot yet, but we're getting close, and all the fundamentals are
there.
We've got a patched version of the Linux kernel and Musl libc running,
complete with virtio devices, memory isolation, and multi-core support.
</p>

<p>
(context switching was particularly painful to implement in such a
constrained environment, but I think the solution ended up being quite
elegant.)
</p>

<p>All that's left to implement before we can boot BusyBox is:</p>
<ul>
<li>the ability to load binaries</li>
<li>somewhere to load them from (virtio in the browser?)</li>
<li>external syscall dispatch</li>
<li>and the ability to jump into userspace</li>
</ul>

<p>
If this sounds interesting, you can check out the source at
<a href="https://github.com/tombl/linux">github.com/tombl/linux</a>
Expand All @@ -105,6 +90,8 @@
<input type="text" name="cmdline" id="cmdline" />
<label for="memory">Memory (in MiB)</label>
<input type="number" name="memory" id="memory" />
<label for="initcpio">URL to an initramfs</label>
<input type="text" name="initcpio" id="initcpio" />
<button type="submit">Reboot</button>
</form>
</details>
Expand Down Expand Up @@ -145,11 +132,14 @@
term.write("Error: not cross origin isolated\n");
}

const { cmdline = "no_hash_pointers console=hvc0", memory = 128 } = Object.fromEntries(
new URLSearchParams(location.search)
);
const {
cmdline = "",
memory = navigator.hardwareConcurrency > 16 ? 256 : 128,
initcpio: initcpioPath = "initramfs.cpio",
} = Object.fromEntries(new URLSearchParams(location.search));
document.querySelector("input[name=cmdline]").value = cmdline;
document.querySelector("input[name=memory]").value = memory;
document.querySelector("input[name=initcpio]").value = initcpioPath;

const stdin = new ReadableStream({
start(controller) {
Expand All @@ -170,6 +160,11 @@
},
});

const initcpio = await fetch(initcpioPath).then((res) => res.ok && res.arrayBuffer());
if (!initcpio) {
term.write(`Failed to fetch initramfs.\n`);
}

const machine = new Machine({
cmdline: cmdline.replace(/\+/g, " "),
memoryMib: memory,
Expand All @@ -178,6 +173,7 @@
new EntropyDevice(),
new BlockDevice(new Uint8Array(8 * 1024 * 1024)),
],
initcpio: new Uint8Array(initcpio),
});

machine.bootConsole.pipeTo(stdout2);
Expand Down
Binary file added tools/wasm/public/initramfs.cpio
Binary file not shown.
17 changes: 12 additions & 5 deletions tools/wasm/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,29 @@ import {
import { parseArgs } from "jsr:@std/cli@1/parse-args";
import { assert } from "./src/util.ts";

const defaultMemory = navigator.hardwareConcurrency > 16 ? 256 : 128;

const args = parseArgs(Deno.args, {
string: ["cmdline"],
boolean: ["help"],
default: {
cmdline: "no_hash_pointers console=hvc0",
memory: 128,
cmdline: "",
memory: defaultMemory,
cpus: navigator.hardwareConcurrency,
initcpio: import.meta.url
.replace("run.js", "public/initramfs.cpio")
.replace("file://", ""),
},
alias: { cmdline: "c", memory: "m", cpus: "j", help: "h" },
alias: { cmdline: "c", memory: "m", initcpio: "i", cpus: "j", help: "h" },
});

if (args.help) {
console.log(`usage: run.ts [options]
options:
-c, --cmdline <string> Command line arguments to pass to the kernel (default: "no_hash_pointers")
-m, --memory <number> Amount of memory to allocate in MiB (default: 128)
-c, --cmdline <string> Command line arguments to pass to the kernel
-m, --memory <number> Amount of memory to allocate in MiB (default: ${defaultMemory})
-i, --initcpio <string> Path to the initramfs to boot
-j, --cpus <number> Number of CPUs to use (default: number of CPUs on the machine)
-h, --help Show this help message
`);
Expand All @@ -43,6 +49,7 @@ const machine = new Machine({
new EntropyDevice(),
new BlockDevice(new Uint8Array(8 * 1024 * 1024)),
],
initcpio: await Deno.readFile(args.initcpio),
});

machine.bootConsole.pipeTo(Deno.stderr.writable, { preventClose: true });
Expand Down
41 changes: 28 additions & 13 deletions tools/wasm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import initramfs from "./build/initramfs_data.cpio";
import sections from "./build/sections.json" with { type: "json" };
import vmlinuxUrl from "./build/vmlinux.wasm";
import { type DeviceTreeNode, generate_devicetree } from "./devicetree.ts";
import init2 from "./init2.cpio";
import { assert, EventEmitter, get_script_path, unreachable } from "./util.ts";
import { virtio_imports, VirtioDevice } from "./virtio.ts";
import { type Imports, type Instance, kernel_imports } from "./wasm.ts";
Expand All @@ -17,7 +16,7 @@ const vmlinux_promise = "compileStreaming" in WebAssembly
? WebAssembly.compileStreaming(vmlinux_response)
: vmlinux_response.then((r) => r.arrayBuffer()).then(WebAssembly.compile);

const INIT2_ADDR = 0x200000;
const INITCPIO_ADDR = 0x200000;

export class Machine extends EventEmitter<{
halt: void;
Expand All @@ -29,6 +28,7 @@ export class Machine extends EventEmitter<{
#workers: Worker[] = [];
#memory: WebAssembly.Memory;
#devices: VirtioDevice[];
#initcpio?: ArrayBufferView;

memory: Uint8Array;
devicetree: DeviceTreeNode;
Expand All @@ -42,11 +42,13 @@ export class Machine extends EventEmitter<{
memoryMib?: number;
cpus?: number;
devices: VirtioDevice[];
initcpio?: ArrayBufferView;
}) {
super();
this.#boot_console = new TransformStream<Uint8Array, Uint8Array>();
this.#boot_console_writer = this.#boot_console.writable.getWriter();
this.#devices = options.devices;
this.#initcpio = options.initcpio;

const PAGE_SIZE = 0x10000;
const BYTES_PER_MIB = 0x100000;
Expand All @@ -65,11 +67,9 @@ export class Machine extends EventEmitter<{
"#size-cells": 1,
chosen: {
"rng-seed": crypto.getRandomValues(new Uint8Array(64)),
bootargs: options.cmdline ?? "no_hash_pointers",
bootargs: `console=hvc0 ${options.cmdline ?? ""}`,
ncpus: options.cpus ?? navigator.hardwareConcurrency,
sections,
"linux,initrd-start": INIT2_ADDR,
"linux,initrd-end": INIT2_ADDR + init2.byteLength,
},
aliases: {},
memory: {
Expand All @@ -83,7 +83,20 @@ export class Machine extends EventEmitter<{
},
};

this.memory.set(init2, INIT2_ADDR);
if (this.#initcpio) {
const chosen = this.devicetree.chosen as DeviceTreeNode;
chosen["linux,initrd-start"] = INITCPIO_ADDR;
chosen["linux,initrd-end"] = INITCPIO_ADDR + this.#initcpio.byteLength;

this.memory.set(
new Uint8Array(
this.#initcpio.buffer,
this.#initcpio.byteOffset,
this.#initcpio.byteLength,
),
INITCPIO_ADDR,
);
}

for (const [i, dev] of this.#devices.entries()) {
this.devicetree[`virtio${i}`] = {
Expand All @@ -97,10 +110,16 @@ export class Machine extends EventEmitter<{
}

async boot() {
const memory_reservations: { address: number; size: number }[] = [];
if (this.#initcpio) {
memory_reservations.push({
address: INITCPIO_ADDR,
size: this.#initcpio.byteLength,
});
}

const devicetree = generate_devicetree(this.devicetree, {
memory_reservations: [
{ address: INIT2_ADDR, size: init2.byteLength },
],
memory_reservations,
});
const vmlinux = await vmlinux_promise;

Expand Down Expand Up @@ -182,10 +201,6 @@ export class Machine extends EventEmitter<{

const instance =
(await WebAssembly.instantiate(vmlinux, imports)) as Instance;
// @ts-expect-error
globalThis.instance = instance;
// @ts-expect-error
globalThis.memory = this.#memory;
instance.exports.boot();
}
}
2 changes: 0 additions & 2 deletions tools/wasm/src/virtio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,10 @@ export abstract class VirtioDevice<Config extends object = object> {
vqs: Virtqueue[] = [];
enable(vq: number, queue: Virtqueue) {
this.vqs[vq] = queue;
console.log("enable", this.constructor.name, vq, queue.size);
}
disable(vq: number) {
const queue = this.vqs[vq];
assert(queue);
console.log("disable", this.constructor.name, vq);
}

abstract notify(vq: number): void;
Expand Down

0 comments on commit 715da7d

Please sign in to comment.