virtual-machine 0.0.2 → 0.0.4

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-GZ343GYI.mjs";
6
+ } from "./chunk-V6I6APCK.mjs";
7
7
  export {
8
8
  NetworkStatus,
9
9
  WasmVm,
package/cli.ts CHANGED
@@ -38,9 +38,26 @@ async function createVm(
38
38
  certHash?: string;
39
39
  },
40
40
  ) {
41
- const resolvedKernel = path.resolve(kernelPath);
42
- const kernelBuf = fs.readFileSync(resolvedKernel);
43
- const kernelBytes = new Uint8Array(kernelBuf);
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
+ }
44
61
 
45
62
  const { WasmInternal } = await import('./');
46
63
  const wasm = await WasmInternal();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virtual-machine",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "bin": "build/cli.js",
6
6
  "publishConfig": {
package/src/lib.rs CHANGED
@@ -8,7 +8,6 @@ pub mod clint;
8
8
  pub mod plic;
9
9
  pub mod uart;
10
10
  pub mod net;
11
- pub mod net_ws;
12
11
  pub mod net_webtransport;
13
12
  pub mod virtio;
14
13
  pub mod emulator;
@@ -16,8 +15,6 @@ pub mod emulator;
16
15
  #[cfg(not(target_arch = "wasm32"))]
17
16
  pub mod console;
18
17
 
19
- #[cfg(not(target_arch = "wasm32"))]
20
- pub mod net_tap;
21
18
 
22
19
  use serde::{Deserialize, Serialize};
23
20
 
@@ -90,24 +87,6 @@ impl WasmVm {
90
87
  let vblk = virtio::VirtioBlock::new(disk_image.to_vec());
91
88
  self.bus.virtio_devices.push(Box::new(vblk));
92
89
  }
93
-
94
- /// Connect to a WebSocket relay server for networking.
95
- /// The URL should be like "ws://localhost:8765".
96
- pub fn connect_network(&mut self, ws_url: &str) -> Result<(), JsValue> {
97
- use crate::net_ws::WsBackend;
98
- use crate::virtio::VirtioNet;
99
-
100
- self.net_status = NetworkStatus::Connecting;
101
-
102
- let backend = WsBackend::new(ws_url);
103
- let mut vnet = VirtioNet::new(Box::new(backend));
104
- vnet.debug = false; // Set to true for debugging
105
-
106
- self.bus.virtio_devices.push(Box::new(vnet));
107
- self.net_status = NetworkStatus::Connected;
108
-
109
- Ok(())
110
- }
111
90
 
112
91
  /// Connect to a WebTransport relay server.
113
92
  pub fn connect_webtransport(&mut self, url: &str, cert_hash: Option<String>) -> Result<(), JsValue> {
package/src/main.rs CHANGED
@@ -70,106 +70,183 @@ fn dump_virtio_id(bus: &mut SystemBus) {
70
70
  );
71
71
  }
72
72
 
73
+ fn print_vm_banner() {
74
+ const BANNER: &str = r#"
75
+ ┌─────────────────────────────────────────────────────────────────────────┐
76
+ │ │
77
+ │ ██████╗ ██╗███████╗██╗ ██╗ ██╗ ██╗ │
78
+ │ ██╔══██╗██║██╔════╝██║ ██╔╝ ██║ ██║ │
79
+ │ ██████╔╝██║███████╗█████╔╝ ██║ ██║ │
80
+ │ ██╔══██╗██║╚════██║██╔═██╗ ╚██╗ ██╔╝ │
81
+ │ ██║ ██║██║███████║██║ ██╗ ╚████╔╝ │
82
+ │ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═╝ ╚═══╝ │
83
+ │ │
84
+ │ RISC-V Virtual Machine Hypervisor v0.1.0 │
85
+ │ 64-bit RISC-V Emulator with VirtIO Support │
86
+ │ │
87
+ └─────────────────────────────────────────────────────────────────────────┘
88
+ "#;
89
+ println!("{}", BANNER);
90
+ }
91
+
92
+ fn print_section(title: &str) {
93
+ println!("\n\x1b[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m");
94
+ println!("\x1b[1;33m ▸ {}\x1b[0m", title);
95
+ println!("\x1b[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m");
96
+ }
97
+
98
+ fn print_status(component: &str, status: &str, ok: bool) {
99
+ let status_color = if ok { "\x1b[1;32m" } else { "\x1b[1;31m" };
100
+ let check = if ok { "✓" } else { "✗" };
101
+ println!(" \x1b[0;37m{:<40}\x1b[0m {}[{}] {}\x1b[0m", component, status_color, check, status);
102
+ }
103
+
104
+ fn print_info(key: &str, value: &str) {
105
+ println!(" \x1b[0;90m├─\x1b[0m \x1b[0;37m{:<20}\x1b[0m \x1b[1;97m{}\x1b[0m", key, value);
106
+ }
107
+
73
108
  fn main() -> Result<(), Box<dyn std::error::Error>> {
74
109
  env_logger::init();
110
+ print_vm_banner();
111
+
75
112
  let args = Args::parse();
76
113
 
114
+ // ─── CPU INITIALIZATION ───────────────────────────────────────────────────
115
+ print_section("CPU INITIALIZATION");
116
+ print_info("Architecture", "RISC-V 64-bit (RV64GC)");
117
+ print_info("Extensions", "I, M, A, F, D, C, Zicsr, Zifencei");
118
+ print_info("Privilege Modes", "Machine, Supervisor, User");
119
+ print_status("CPU Core", "INITIALIZED", true);
120
+
121
+ // ─── MEMORY SUBSYSTEM ─────────────────────────────────────────────────────
122
+ print_section("MEMORY SUBSYSTEM");
123
+ let dram_size_bytes = args
124
+ .mem_mib
125
+ .checked_mul(1024 * 1024)
126
+ .ok_or("Requested memory size is too large")?;
127
+ let dram_base = 0x8000_0000u64;
128
+
129
+ print_info("DRAM Base", &format!("0x{:08X}", dram_base));
130
+ print_info("DRAM Size", &format!("{} MiB ({} bytes)", args.mem_mib, dram_size_bytes));
131
+ print_info("Address Range", &format!("0x{:08X} - 0x{:08X}", dram_base, dram_base + dram_size_bytes as u64));
132
+
133
+ let mut bus = SystemBus::new(dram_base, dram_size_bytes);
134
+ print_status("DRAM Controller", "ONLINE", true);
135
+ print_status("MMU (Sv39)", "READY", true);
136
+
137
+ // ─── KERNEL LOADING ───────────────────────────────────────────────────────
138
+ print_section("KERNEL LOADING");
77
139
  let buffer = if args.kernel.starts_with("http://") || args.kernel.starts_with("https://") {
78
- println!("Downloading kernel from {}...", args.kernel);
140
+ print_info("Source", "Remote (HTTP/HTTPS)");
141
+ print_info("URL", &args.kernel);
142
+ println!(" \x1b[0;90m├─\x1b[0m \x1b[0;33mDownloading...\x1b[0m");
79
143
  let response = reqwest::blocking::get(&args.kernel)?;
80
144
  if !response.status().is_success() {
145
+ print_status("Download", "FAILED", false);
81
146
  return Err(format!("Failed to download kernel: {}", response.status()).into());
82
147
  }
83
- response.bytes()?.to_vec()
148
+ let bytes = response.bytes()?.to_vec();
149
+ print_status("Download", "COMPLETE", true);
150
+ bytes
84
151
  } else {
152
+ print_info("Source", "Local filesystem");
153
+ print_info("Path", &args.kernel);
85
154
  let mut file = File::open(&args.kernel)?;
86
155
  let mut buffer = Vec::new();
87
156
  file.read_to_end(&mut buffer)?;
88
157
  buffer
89
158
  };
159
+ print_info("Kernel Size", &format!("{} bytes ({:.2} KiB)", buffer.len(), buffer.len() as f64 / 1024.0));
90
160
 
91
- let dram_size_bytes = args
92
- .mem_mib
93
- .checked_mul(1024 * 1024)
94
- .ok_or("Requested memory size is too large")?;
95
-
96
- // Initialize DRAM at 0x8000_0000
97
- let dram_base = 0x8000_0000;
98
- let mut bus = SystemBus::new(dram_base, dram_size_bytes);
99
161
 
162
+ // ─── VIRTIO DEVICE BUS ─────────────────────────────────────────────────────
163
+ print_section("VIRTIO DEVICE BUS");
164
+ print_info("Bus Type", "VirtIO MMIO v2");
165
+ print_info("Base Address", "0x10001000");
166
+ print_info("Device Spacing", "0x1000 (4 KiB)");
167
+
100
168
  // If a disk image is provided, wire up VirtIO Block at 0x1000_1000
101
169
  if let Some(disk_path) = &args.disk {
102
170
  let mut disk_file = File::open(disk_path)?;
103
171
  let mut disk_buf = Vec::new();
104
172
  disk_file.read_to_end(&mut disk_buf)?;
173
+ let disk_size_mib = disk_buf.len() / (1024 * 1024);
105
174
  let vblk = riscv_vm::virtio::VirtioBlock::new(disk_buf);
106
175
  bus.virtio_devices.push(Box::new(vblk));
107
- println!("VirtIO Block device attached at 0x1000_1000 (IRQ 1)");
176
+ println!();
177
+ println!(" \x1b[1;35m┌─ VirtIO Block Device ─────────────────────────────────┐\x1b[0m");
178
+ println!(" \x1b[1;35m│\x1b[0m Address: \x1b[1;97m0x10001000\x1b[0m \x1b[1;35m│\x1b[0m");
179
+ println!(" \x1b[1;35m│\x1b[0m IRQ: \x1b[1;97m1\x1b[0m \x1b[1;35m│\x1b[0m");
180
+ println!(" \x1b[1;35m│\x1b[0m Disk Size: \x1b[1;97m{} MiB\x1b[0m \x1b[1;35m│\x1b[0m", disk_size_mib);
181
+ println!(" \x1b[1;35m│\x1b[0m Image: \x1b[0;90m{}\x1b[0m", disk_path.display());
182
+ println!(" \x1b[1;35m└────────────────────────────────────────────────────────┘\x1b[0m");
183
+ print_status("VirtIO Block", "ATTACHED", true);
108
184
  }
109
185
 
110
- // If a TAP interface is provided, wire up VirtIO Net with TAP backend
111
- if let Some(tap_name) = &args.net_tap {
112
- let tap_backend = riscv_vm::net_tap::TapBackend::new(tap_name);
113
- let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(tap_backend));
114
- let device_idx = bus.virtio_devices.len();
115
- let irq = 1 + device_idx; // IRQ 1 for first device, 2 for second, etc.
116
- bus.virtio_devices.push(Box::new(vnet));
117
- let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
118
- println!("VirtIO Net device (TAP: {}) attached at 0x{:x} (IRQ {})", tap_name, base_addr, irq);
119
- } else if let Some(ws_url) = &args.net_ws {
120
- // Wire up VirtIO Net with WebSocket backend
121
- let ws_backend = riscv_vm::net_ws::WsBackend::new(ws_url);
122
- let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(ws_backend));
123
- let device_idx = bus.virtio_devices.len();
124
- let irq = 1 + device_idx;
125
- bus.virtio_devices.push(Box::new(vnet));
126
- let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
127
- println!("VirtIO Net device (WebSocket: {}) attached at 0x{:x} (IRQ {})", ws_url, base_addr, irq);
128
- } else if let Some(wt_url) = &args.net_webtransport {
129
- // Wire up VirtIO Net with WebTransport backend
186
+ // If WebTransport is provided, wire up VirtIO Net
187
+ if let Some(wt_url) = &args.net_webtransport {
130
188
  let wt_backend = riscv_vm::net_webtransport::WebTransportBackend::new(wt_url, args.net_cert_hash.clone());
131
189
  let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(wt_backend));
132
190
  let device_idx = bus.virtio_devices.len();
133
191
  let irq = 1 + device_idx;
134
192
  bus.virtio_devices.push(Box::new(vnet));
135
193
  let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
136
- println!("VirtIO Net device (WebTransport: {}) attached at 0x{:x} (IRQ {})", wt_url, base_addr, irq);
137
- } else if args.net_dummy {
138
- // Wire up VirtIO Net with dummy backend (for testing)
139
- let dummy_backend = riscv_vm::net::DummyBackend::new();
140
- let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(dummy_backend));
141
- let device_idx = bus.virtio_devices.len();
142
- let irq = 1 + device_idx;
143
- bus.virtio_devices.push(Box::new(vnet));
144
- let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
145
- println!("VirtIO Net device (Dummy) attached at 0x{:x} (IRQ {})", base_addr, irq);
146
- }
194
+ println!();
195
+ println!(" \x1b[1;34m┌─ VirtIO Network Device ───────────────────────────────┐\x1b[0m");
196
+ println!(" \x1b[1;34m│\x1b[0m Address: \x1b[1;97m0x{:08X}\x1b[0m \x1b[1;34m│\x1b[0m", base_addr);
197
+ println!(" \x1b[1;34m│\x1b[0m IRQ: \x1b[1;97m{}\x1b[0m \x1b[1;34m│\x1b[0m", irq);
198
+ println!(" \x1b[1;34m│\x1b[0m Backend: \x1b[1;97mWebTransport\x1b[0m \x1b[1;34m│\x1b[0m");
199
+ println!(" \x1b[1;34m│\x1b[0m Relay: \x1b[0;90m{}\x1b[0m", wt_url);
200
+ println!(" \x1b[1;34m└────────────────────────────────────────────────────────┘\x1b[0m");
201
+ print_status("VirtIO Network", "ATTACHED", true);
202
+ }
147
203
 
148
204
  let entry_pc = if buffer.starts_with(b"\x7FELF") {
149
- println!("Detected ELF payload, loading program segments...");
205
+ print_info("Format", "ELF64 Executable");
150
206
  load_elf_into_dram(&buffer, &mut bus)?
151
207
  } else {
152
208
  if args.load_addr < dram_base {
153
- eprintln!("Load address must be >= 0x{:x}", dram_base);
209
+ print_status("Load Address", "INVALID (below DRAM)", false);
154
210
  return Ok(());
155
211
  }
156
212
  let offset = args.load_addr - dram_base;
157
- println!(
158
- "Loading raw binary ({} bytes) at 0x{:x}",
159
- buffer.len(),
160
- args.load_addr
161
- );
213
+ print_info("Format", "Raw Binary");
214
+ print_info("Load Address", &format!("0x{:08X}", args.load_addr));
162
215
  bus.dram
163
216
  .load(&buffer, offset)
164
217
  .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
165
218
  args.load_addr
166
219
  };
220
+ print_info("Entry Point", &format!("0x{:08X}", entry_pc));
221
+ print_status("Kernel Image", "LOADED", true);
222
+
223
+ // ─── SYSTEM PERIPHERALS ───────────────────────────────────────────────────
224
+ print_section("SYSTEM PERIPHERALS");
225
+ print_info("UART 16550", "0x10000000 (IRQ 10)");
226
+ print_info("CLINT", "0x02000000 (Machine Timer)");
227
+ print_info("PLIC", "0x0C000000 (IRQ Controller)");
228
+ print_status("UART Console", "READY", true);
229
+ print_status("Interrupt Controller", "CONFIGURED", true);
230
+
231
+ // Early probe dump (harmless if device absent)
232
+ dump_virtio_id(&mut bus);
167
233
 
168
234
  let mut cpu = Cpu::new(entry_pc);
169
235
 
170
- println!("Starting execution at 0x{:x}", cpu.pc);
171
- // Early probe dump (harmless if device absent): helps debug xv6 panic on probe.
172
- dump_virtio_id(&mut bus);
236
+ // ─── BOOT SEQUENCE COMPLETE ───────────────────────────────────────────────
237
+ print_section("BOOT SEQUENCE COMPLETE");
238
+ println!();
239
+ println!(" \x1b[1;32m╔══════════════════════════════════════════════════════════════════════╗\x1b[0m");
240
+ println!(" \x1b[1;32m║\x1b[0m \x1b[1;32m║\x1b[0m");
241
+ println!(" \x1b[1;32m║\x1b[0m \x1b[1;97mStarting RISC-V Kernel at 0x{:08X}\x1b[0m \x1b[1;32m║\x1b[0m", entry_pc);
242
+ println!(" \x1b[1;32m║\x1b[0m \x1b[0;90mPress Ctrl-A then 'x' to terminate\x1b[0m \x1b[1;32m║\x1b[0m");
243
+ println!(" \x1b[1;32m║\x1b[0m \x1b[1;32m║\x1b[0m");
244
+ println!(" \x1b[1;32m╚══════════════════════════════════════════════════════════════════════╝\x1b[0m");
245
+ println!();
246
+ println!("\x1b[1;36m══════════════════════════════════════════════════════════════════════════════\x1b[0m");
247
+ println!("\x1b[1;33m KERNEL OUTPUT BEGINS\x1b[0m");
248
+ println!("\x1b[1;36m══════════════════════════════════════════════════════════════════════════════\x1b[0m");
249
+ println!();
173
250
 
174
251
  let mut step_count = 0u64;
175
252
  let mut last_report_step = 0u64;
@@ -222,8 +299,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
222
299
  if let Err(trap) = step_result {
223
300
  match trap {
224
301
  // Test finisher / explicit host stop requested by the guest.
225
- Trap::RequestedTrap(code) => {
226
- println!("Guest requested stop via test finisher: 0x{code:x}");
302
+ Trap::RequestedTrap(_) => {
227
303
  break;
228
304
  }
229
305
  // Non-recoverable emulator error: dump state and exit.
@@ -6,7 +6,6 @@
6
6
  //! - 0x01 prefix: Ethernet data frames
7
7
 
8
8
  use crate::net::NetworkBackend;
9
- use std::sync::Arc;
10
9
 
11
10
  /// Message type prefix for control messages
12
11
  const MSG_TYPE_CONTROL: u8 = 0x00;
@@ -68,6 +67,7 @@ fn decode_message(data: &[u8]) -> Option<Vec<u8>> {
68
67
  #[cfg(not(target_arch = "wasm32"))]
69
68
  mod native {
70
69
  use super::*;
70
+ use std::sync::Arc;
71
71
  use std::sync::atomic::{AtomicBool, Ordering};
72
72
  use std::sync::mpsc::{channel, Receiver, Sender};
73
73
  use std::thread;
package/src/uart.rs CHANGED
@@ -7,9 +7,7 @@ pub const UART_SIZE: u64 = 0x100;
7
7
  // Registers (offset)
8
8
  const RBR: u64 = 0x00; // Receiver Buffer (Read)
9
9
  const THR: u64 = 0x00; // Transmitter Holding (Write)
10
- const DLL: u64 = 0x00; // Divisor Latch LSB (Read/Write if DLAB=1)
11
10
  const IER: u64 = 0x01; // Interrupt Enable
12
- const DLM: u64 = 0x01; // Divisor Latch MSB (Read/Write if DLAB=1)
13
11
  const IIR: u64 = 0x02; // Interrupt Identity (Read)
14
12
  const FCR: u64 = 0x02; // FIFO Control (Write)
15
13
  const LCR: u64 = 0x03; // Line Control