virtual-machine 0.0.4 → 0.0.10

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.
@@ -3,7 +3,7 @@ import {
3
3
  WasmVm,
4
4
  initSync,
5
5
  riscv_vm_default
6
- } from "./chunk-V6I6APCK.mjs";
6
+ } from "./chunk-MELUIZWO.mjs";
7
7
  export {
8
8
  NetworkStatus,
9
9
  WasmVm,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virtual-machine",
3
- "version": "0.0.4",
3
+ "version": "0.0.10",
4
4
  "description": "",
5
5
  "bin": "build/cli.js",
6
6
  "publishConfig": {
@@ -9,20 +9,27 @@
9
9
  "main": "./build/index.js",
10
10
  "module": "./build/index.mjs",
11
11
  "types": "./build/index.d.ts",
12
+ "files": [
13
+ "build"
14
+ ],
12
15
  "scripts": {
13
16
  "build": "rm -rf build && sh build.sh"
14
17
  },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/elribonazo/riscv-vm"
21
+ },
15
22
  "author": "elribonazo@gmail.com",
16
23
  "license": "ISC",
17
24
  "dependencies": {
18
- "@trust0/ridb-build": "^0.0.21",
19
- "tsup": "^8.4.0",
20
- "typescript": "^5.8.3",
21
25
  "yargs": "^18.0.0"
22
26
  },
23
27
  "packageManager": "yarn@4.11.0+sha512.4e54aeace9141df2f0177c266b05ec50dc044638157dae128c471ba65994ac802122d7ab35bcd9e81641228b7dcf24867d28e750e0bcae8a05277d600008ad54",
24
28
  "devDependencies": {
25
29
  "@esbuild-plugins/node-resolve": "^0.2.2",
26
- "@types/node": "^24.10.1"
30
+ "@trust0/ridb-build": "^0.0.21",
31
+ "@types/node": "^24.10.1",
32
+ "tsup": "^8.4.0",
33
+ "typescript": "^5.8.3"
27
34
  }
28
35
  }
Binary file
package/.yarnrc.yml DELETED
@@ -1 +0,0 @@
1
- nodeLinker: node-modules
package/Cargo.toml DELETED
@@ -1,49 +0,0 @@
1
- [package]
2
- name = "riscv-vm"
3
- version = "0.0.4"
4
- edition = "2024"
5
-
6
- [lib]
7
- crate-type = ["cdylib", "rlib"]
8
-
9
- [dependencies]
10
- log = "0.4"
11
- thiserror = "1.0"
12
- hex = "0.4"
13
- env_logger = "0.10"
14
- clap = { version = "4.4", features = ["derive"] }
15
- libc = "0.2"
16
- goblin = "0.8"
17
- serde = { version = "1.0", features = ["derive"] }
18
- bincode = "1.3"
19
- sha2 = "0.10"
20
- wasm-bindgen = "0.2"
21
-
22
- [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
23
- tun-tap = "0.1"
24
- reqwest = { version = "0.12", features = ["blocking", "rustls-tls"] }
25
- tungstenite = "0.21"
26
- # WebTransport for connecting to the relay
27
- wtransport = { version = "0.6", features = ["dangerous-configuration"] }
28
- tokio = { version = "1", features = ["full"] }
29
- futures = "0.3"
30
-
31
- [target.'cfg(target_arch = "wasm32")'.dependencies]
32
- console_error_panic_hook = "0.1"
33
- web-sys = { version = "0.3", features = [
34
- "WebSocket",
35
- "MessageEvent",
36
- "BinaryType",
37
- "ErrorEvent",
38
- "CloseEvent",
39
- "WebTransport",
40
- "WebTransportOptions",
41
- "WebTransportHash",
42
- "WebTransportDatagramDuplexStream",
43
- "ReadableStream",
44
- "WritableStream",
45
- "ReadableStreamDefaultReader",
46
- "WritableStreamDefaultWriter"
47
- ] }
48
- js-sys = "0.3"
49
- wasm-bindgen-futures = "0.4"
package/build.sh DELETED
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e # Exit on any error
3
-
4
- is_mac() {
5
- [[ "$OSTYPE" == "darwin"* ]]
6
- }
7
-
8
- PACKAGEJSON=./pkg/package.json
9
- IMPORTFILE=./pkg/riscv_vm.js
10
-
11
- echo "Building the rust library"
12
- RUSTFLAGS=--cfg=web_sys_unstable_apis npx wasm-pack build --target web
13
-
14
- if is_mac; then
15
- sed -i '' 's/"module": "ridb_core.js",/"main": "ridb_core.js",/' $PACKAGEJSON
16
- sed -i '' "/if (typeof module_or_path === 'undefined') {/,/}/d" $IMPORTFILE
17
- else
18
- sed -i 's/"module": "ridb_core.js",/"main": "ridb_core.js",/' $PACKAGEJSON
19
- sed -i "/if (typeof module_or_path === 'undefined') {/,/}/d" $IMPORTFILE
20
- fi
21
-
22
- npx tsup --config tsup/tsup.cli.ts
23
- npx tsup --config tsup/tsup.core.cjs.ts
24
- npx tsup --config tsup/tsup.core.esm.ts
25
- npx tsup --config tsup/tsup.core.cjs.ts --dts-only
package/cli.ts DELETED
@@ -1,268 +0,0 @@
1
- #!/usr/bin/env node
2
- /// <reference types="node" />
3
- /**
4
- * RISC-V VM CLI
5
- *
6
- * This CLI mirrors the browser VM wiring in `useVM`:
7
- * - loads a kernel image (ELF or raw binary)
8
- * - optionally loads a VirtIO block disk image (e.g. xv6 `fs.img`)
9
- * - can optionally connect to a network relay (WebTransport/WebSocket)
10
- * - runs the VM in a tight loop
11
- * - connects stdin → UART input and UART output → stdout
12
- */
13
-
14
- import fs from 'node:fs';
15
- import path from 'node:path';
16
- import yargs from 'yargs';
17
- import { hideBin } from 'yargs/helpers';
18
-
19
- // Default relay server URL and cert hash, mirroring the React hook.
20
- const DEFAULT_RELAY_URL =
21
- process.env.RELAY_URL || 'https://localhost:4433';
22
- const DEFAULT_CERT_HASH =
23
- process.env.RELAY_CERT_HASH || '';
24
-
25
- /**
26
- * Create and initialize a Wasm VM instance, mirroring the React `useVM` hook:
27
- * - initializes the WASM module once via `WasmInternal`
28
- * - constructs `WasmVm` with the kernel bytes
29
- * - optionally attaches a VirtIO block device from a disk image
30
- * - optionally connects to a network relay (WebTransport/WebSocket)
31
- */
32
- async function createVm(
33
- kernelPath: string,
34
- diskPath?: string,
35
- options?: {
36
- network?: boolean;
37
- relayUrl?: string;
38
- certHash?: string;
39
- },
40
- ) {
41
- let kernelBytes: Uint8Array;
42
-
43
- if (kernelPath.startsWith('http://') || kernelPath.startsWith('https://')) {
44
- console.error(`[CLI] Downloading kernel from ${kernelPath}...`);
45
- const response = await fetch(kernelPath);
46
- if (!response.ok) {
47
- throw new Error(
48
- `Failed to fetch kernel from ${kernelPath}: ${response.statusText}`,
49
- );
50
- }
51
- const arrayBuffer = await response.arrayBuffer();
52
- kernelBytes = new Uint8Array(arrayBuffer);
53
- } else {
54
- const resolvedKernel = path.resolve(kernelPath);
55
- if (!fs.existsSync(resolvedKernel)) {
56
- throw new Error(`Kernel file not found at ${resolvedKernel}`);
57
- }
58
- const kernelBuf = fs.readFileSync(resolvedKernel);
59
- kernelBytes = new Uint8Array(kernelBuf);
60
- }
61
-
62
- const { WasmInternal } = await import('./');
63
- const wasm = await WasmInternal();
64
- const VmCtor = wasm.WasmVm;
65
- if (!VmCtor) {
66
- throw new Error('WasmVm class not found in wasm module');
67
- }
68
-
69
- const vm = new VmCtor(kernelBytes);
70
-
71
- if (diskPath) {
72
- const resolvedDisk = path.resolve(diskPath);
73
- const diskBuf = fs.readFileSync(resolvedDisk);
74
- const diskBytes = new Uint8Array(diskBuf);
75
-
76
- if (typeof vm.load_disk === 'function') {
77
- vm.load_disk(diskBytes);
78
- }
79
- }
80
-
81
- // Optional network setup (pre-boot), mirroring `useVM.startVM`.
82
- if (options?.network) {
83
- const relayUrl = options.relayUrl || DEFAULT_RELAY_URL;
84
- const certHash = options.certHash || DEFAULT_CERT_HASH || undefined;
85
-
86
- try {
87
- if (typeof (vm as any).connect_webtransport === 'function') {
88
- (vm as any).connect_webtransport(relayUrl, certHash);
89
- console.error(
90
- `[Network] Initiating WebTransport connection to ${relayUrl}`,
91
- );
92
- } else if (typeof (vm as any).connect_network === 'function') {
93
- (vm as any).connect_network(relayUrl);
94
- console.error(
95
- `[Network] Initiating WebSocket connection to ${relayUrl}`,
96
- );
97
- } else {
98
- console.error(
99
- '[Network] No network methods available on VM (rebuild WASM with networking)',
100
- );
101
- }
102
- } catch (err) {
103
- console.error('[Network] Pre-boot connection failed:', err);
104
- }
105
- }
106
-
107
- return vm;
108
- }
109
-
110
- /**
111
- * Run the VM in a loop and wire stdin/stdout to the UART, similar to the browser loop:
112
- * - executes a fixed number of instructions per tick
113
- * - drains the UART output buffer and writes to stdout
114
- * - feeds raw stdin bytes into the VM's UART input
115
- */
116
- function runVmLoop(vm: any) {
117
- let running = true;
118
-
119
- const shutdown = (code: number) => {
120
- if (!running) return;
121
- running = false;
122
-
123
- if (process.stdin.isTTY && (process.stdin as any).setRawMode) {
124
- (process.stdin as any).setRawMode(false);
125
- }
126
- process.stdin.pause();
127
-
128
- process.exit(code);
129
- };
130
-
131
- // Handle Ctrl+C via signal as a fallback
132
- process.on('SIGINT', () => {
133
- shutdown(0);
134
- });
135
-
136
- // Configure stdin → VM UART input
137
- if (process.stdin.isTTY && (process.stdin as any).setRawMode) {
138
- (process.stdin as any).setRawMode(true);
139
- }
140
- process.stdin.resume();
141
-
142
- process.stdin.on('data', (chunk) => {
143
- // In raw mode `chunk` is typically a Buffer; iterate its bytes.
144
- for (const byte of chunk as any as Uint8Array) {
145
- // Ctrl+C (ETX) – terminate the VM and exit
146
- if (byte === 3) {
147
- shutdown(0);
148
- return;
149
- }
150
-
151
- // Map CR to LF as in the React hook
152
- if (byte === 13) {
153
- vm.input(10);
154
- } else if (byte === 127 || byte === 8) {
155
- // Backspace
156
- vm.input(8);
157
- } else {
158
- vm.input(byte);
159
- }
160
- }
161
- });
162
-
163
- const INSTRUCTIONS_PER_TICK = 100_000;
164
-
165
- const loop = () => {
166
- if (!running) return;
167
-
168
- try {
169
- // Execute a batch of instructions
170
- for (let i = 0; i < INSTRUCTIONS_PER_TICK; i++) {
171
- vm.step();
172
- }
173
-
174
- // Drain UART output buffer, similar to `useVM`
175
- const outChunks: string[] = [];
176
- let limit = 2000;
177
- let code = typeof vm.get_output === 'function' ? vm.get_output() : undefined;
178
-
179
- while (code !== undefined && limit-- > 0) {
180
- const c = Number(code);
181
-
182
- if (c === 8) {
183
- // Backspace – move cursor back, erase, move back
184
- outChunks.push('\b \b');
185
- } else if (c === 10 || c === 13) {
186
- outChunks.push('\n');
187
- } else if (c >= 32 && c <= 126) {
188
- outChunks.push(String.fromCharCode(c));
189
- }
190
-
191
- code = vm.get_output();
192
- }
193
-
194
- if (outChunks.length) {
195
- process.stdout.write(outChunks.join(''));
196
- }
197
- } catch (err) {
198
- console.error('\n[VM] Error while executing:', err);
199
- shutdown(1);
200
- return;
201
- }
202
-
203
- // Schedule the next tick; run as fast as the event loop allows.
204
- setImmediate(loop);
205
- };
206
-
207
- loop();
208
- }
209
-
210
- (yargs(hideBin(process.argv)) as any)
211
- .command(
212
- 'run <kernel>',
213
- 'Runs a RISC-V kernel inside the virtual machine',
214
- (y: any) =>
215
- y
216
- .positional('kernel', {
217
- type: 'string',
218
- describe: 'Path to the RISC-V kernel (ELF or raw binary)',
219
- demandOption: true,
220
- })
221
- .option('disk', {
222
- alias: 'd',
223
- type: 'string',
224
- describe: 'Optional path to a VirtIO block disk image (e.g. xv6 fs.img)',
225
- })
226
- .option('network', {
227
- alias: 'n',
228
- type: 'boolean',
229
- describe:
230
- 'Enable network and connect to relay at boot (uses WebTransport/WebSocket)',
231
- })
232
- .option('relay-url', {
233
- alias: 'r',
234
- type: 'string',
235
- describe: `Relay URL for WebTransport/WebSocket (default: ${DEFAULT_RELAY_URL})`,
236
- })
237
- .option('cert-hash', {
238
- alias: 'c',
239
- type: 'string',
240
- describe:
241
- 'Optional certificate SHA-256 hash for self-signed TLS (used with WebTransport)',
242
- }),
243
- async (argv: any) => {
244
- const kernelPath = argv.kernel as string;
245
- const diskPath = (argv.disk ?? undefined) as string | undefined;
246
- const relayUrlArg = (argv['relay-url'] ?? undefined) as string | undefined;
247
- const certHashArg = (argv['cert-hash'] ?? undefined) as string | undefined;
248
- // If user explicitly passes --network or a relay URL, enable networking.
249
- const enableNetwork =
250
- (argv.network as boolean | undefined) ?? !!relayUrlArg;
251
-
252
- try {
253
- const vm = await createVm(kernelPath, diskPath, {
254
- network: enableNetwork,
255
- relayUrl: relayUrlArg,
256
- certHash: certHashArg,
257
- });
258
- runVmLoop(vm);
259
- } catch (err) {
260
- console.error('[CLI] Failed to start VM:', err);
261
- process.exit(1);
262
- }
263
- },
264
- )
265
- .demandCommand(1, 'You need to specify a command')
266
- .strict()
267
- .help()
268
- .parse();
package/index.ts DELETED
@@ -1,16 +0,0 @@
1
- import wasmBuffer from "./pkg/riscv_vm_bg.wasm";
2
-
3
- let loaded: typeof import("./pkg/riscv_vm") | undefined;
4
-
5
- export async function WasmInternal() {
6
- if (!loaded) {
7
- const module = await import("./pkg/riscv_vm");
8
- const wasmInstance = module.initSync(wasmBuffer);
9
- await module.default(wasmInstance);
10
- loaded = module;
11
- }
12
- return loaded;
13
- }
14
-
15
-
16
- export { NetworkStatus, WasmVm } from "./pkg/riscv_vm";